123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101# 1 "src/meshFC.ml"(* Functions for layout fortran_layout.
***********************************************************************)openPrintfopenBigarrayopenMesh_commontypemesh=fortran_layoutttypevec=fortran_layoutMesh_common.vectypemat=fortran_layoutMesh_common.mattypeint_mat=fortran_layoutMesh_common.int_mattypeint_vec=fortran_layoutMesh_common.int_vecletlayout=fortran_layout;;letempty_vec=Array1.createintlayout0letempty_mat2=Array2.create(float64)fortran_layout(2)(0)letempty_mat4=Array2.create(float64)fortran_layout(4)(0)letpslg~hole~region~point_marker~point~segment_marker~segment=letpoint_marker=matchpoint_markerwith|None->empty_vec|Somem->letn=Array1.dimminif0<n&&n<Array2.dim2(point)theninvalid_arg"Mesh.pslg: point_marker too small";minletsegment_marker=matchsegment_markerwith|None->empty_vec|Somem->letn=Array1.dimminif0<n&&n<Array2.dim2(segment)theninvalid_arg"Mesh.pslg: segment_marker too small";minlethole=matchholewith|None->empty_mat2|Someh->ifArray2.dim2(h)>0&&Array2.dim1(h)<>2theninvalid_arg"Mesh.pslg: dim1 hole must be 2";hinletregion=matchregionwith|None->empty_mat4|Somer->ifArray2.dim2(r)>0&&Array2.dim1(r)<>4theninvalid_arg"Mesh.pslg: dim1 region must be 4";rin(objectmethodpoint=pointmethodpoint_marker=point_markermethodsegment=segmentmethodsegment_marker=segment_markermethodhole=holemethodregion=regionend:fortran_layoutpslg)(** Return the smaller box (xmin, xmax, ymin, ymax) containing the [mesh]. *)letbounding_box(mesh:mesh)=letxmin=refinfinityandxmax=refneg_infinityandymin=refinfinityandymax=refneg_infinityinletpoint=mesh#pointinfori=1toArray2.dim2(point)doletx=point.{1,i}andy=point.{2,i}inifx>!xmaxthenxmax:=x;ifx<!xminthenxmin:=x;ify>!ymaxthenymax:=y;ify<!yminthenymin:=y;done;(!xmin,!xmax,!ymin,!ymax)letlatex_write?edge:(edge_color=fun_->Someblack)(mesh:mesh)fh=letedge=mesh#edgeinletpt=mesh#pointinifArray2.dim2(edge)=0theninvalid_arg"Mesh.latex: mesh#edge must be nonempty";ifArray2.dim1(edge)<>2theninvalid_arg"Mesh.latex: mesh#edge must have 2 rows (fortran)";ifArray2.dim2(pt)=0theninvalid_arg"Mesh.latex: mesh#point must be nonempty";ifArray2.dim1(pt)<>2theninvalid_arg"Mesh.latex: mesh#point must have 2 rows (fortran)";letxmin,xmax,ymin,ymax=bounding_boxmeshinlatex_beginfh(xmax-.xmin)(ymax-.ymin)xminymin;(* Write lines *)fprintffh" %% %i triangles\n"(Array2.dim2(mesh#triangle));fore=1toArray2.dim2(edge)domatchedge_colorewith|None->()|Somecolor->leti1=edge.{1,e}andi2=edge.{2,e}inletp1={x=pt.{1,i1};y=pt.{2,i1}}andp2={x=pt.{1,i2};y=pt.{2,i2}}inlinefhcolorp1p2done;(* Write points *)fprintffh" %% %i points\n"(Array2.dim2(pt));fori=1toArray2.dim2(pt)dopoint_xyfhi(pt.{1,i})(pt.{2,i});done;latex_endfhletlatex?edgemeshfilename=letfh=open_outfilenameintrylatex_write?edgemeshfh;close_outfhwithe->close_outfh;raiseeletscilab(mesh:mesh)?(longitude=70.)?(azimuth=60.)?(mode=`Triangles)?(box=`Full)?edgecolor(z:vec)fname=lettriangle=mesh#triangleinletpt=mesh#pointinifArray2.dim2(triangle)=0theninvalid_arg"Mesh.scilab: mesh#triangle must be nonempty";ifArray2.dim1(triangle)<3theninvalid_arg"Mesh.scilab: mesh#triangle must have at least \
3 rows (fortran)";ifArray2.dim2(pt)=0theninvalid_arg"Mesh.scilab: mesh#point must be nonempty";ifArray2.dim1(pt)<>2theninvalid_arg"Mesh.scilab: mesh#point must have 2 rows (fortran)";ifArray1.dimz<Array2.dim2(pt)theninvalid_arg"Mesh.scilab: vector too small";letfname=ifFilename.check_suffixfname".sci"thenFilename.chop_extensionfnameelsefnameinletsci=fname^".sci"andxf=fname^"_x.dat"andyf=fname^"_y.dat"andzf=fname^"_z.dat"inletmode=matchmodewith|`Triangles->1|`Triangles_only->0|`No_triangles->-1inletbox=matchboxwith|`None->0|`Behind->2|`Box_only->3|`Full->4inletedgecolor,er,eg,eb=matchedgecolorwith|None->false,0.,0.,0.|Some(`Colorc)->(true,float((clsr16)land0xFF)/.255.,float((clsr8)land0xFF)/.255.,float(cland0xFF)/.255.)|Some(`Greyc)->ifc<=0.||c>1.then(false,0.,0.,0.)else(true,c,c,c)inletfh=open_outsciin(* Put the edge color at the bottom of the colormap so it is usually
hidden. Moreover, put enough color in the map so the edge color
is seldom drawn. *)fprintffh"mode(0);\n\
// Run in Scilab with: exec('%s')\n\
// Written by the OCaml Mesh module (version 0.9.0).\n\
// mesh: %i triangles, %i points.\n\
ocaml = struct('f', scf(), 'e', null, \
'x', fscanfMat('%s'), 'y', fscanfMat('%s'), \
'z', fscanfMat('%s'));\n\
clf();\n\
ocaml.e = gce();\n\
ocaml.e.hiddencolor = -1;\n\
ocaml.f.color_map = jetcolormap(100);\n"sci(Array2.dim2(triangle))(Array2.dim2(pt))(Filename.basenamexf)(Filename.basenameyf)(Filename.basenamezf);ifedgecolor&&mode>=0thenfprintffh"ocaml.f.color_map(1,:) = [%g, %g, %g];\n\
xset('color', 1);\n"eregeb;fprintffh"plot3d1(ocaml.x, ocaml.y, ocaml.z, theta=%g, alpha=%g, \
flag=[%d,2,%d]);\n\
disp('Save: xs2pdf(ocaml.f, ''%s.pdf'')');\n"longitudeazimuthmodeboxfname;close_outfh;letsave_matfnamecoord=letfh=open_outfnamein(* We traverse several times the triangles but Scilab will not
have to transpose the matrices. *)forpoint=1to3dofort=1toArray2.dim2(triangle)dofprintffh"%.16e "(coord(triangle.{point,t}))done;fprintffh"\n";done;close_outfhinsave_matxf(funi->pt.{1,i});save_matyf(funi->pt.{2,i});save_matzf(funi->z.{i})letis_allowed_matlabc=('0'<=c&&c<='9')||('a'<=c&&c<='z')||('A'<=c&&c<='Z')||c='_'letmatlab(mesh:mesh)?(edgecolor=`Color0)?(linestyle="-")?(facealpha=1.)(z:vec)fname=lettr=mesh#triangleinletpt=mesh#pointinifArray2.dim2(tr)=0theninvalid_arg"Mesh.matlab: mesh#triangle must be nonempty";ifArray2.dim1(tr)<3theninvalid_arg"Mesh.matlab: mesh#triangle must have at least \
3 rows (fortran)";ifArray2.dim2(pt)=0theninvalid_arg"Mesh.matlab: mesh#point must be nonempty";ifArray2.dim1(pt)<>2theninvalid_arg"Mesh.matlab: mesh#point must have 2 rows (fortran)";letdir=Filename.dirnamefnameandbase=Filename.basenamefnameinletbase=ifFilename.check_suffixbase".m"thenBytes.unsafe_of_string(String.subbase0(String.lengthbase-2))elseBytes.of_stringbasein(* Matlab filenames can contain only alphanumeric characters and
underscores. Convert all other characters to underscore *)fori=0toBytes.lengthbase-1doifnot(is_allowed_matlab(Bytes.getbasei))thenBytes.setbasei'_'done;letbase=Bytes.unsafe_to_stringbaseinletmat=Filename.concatdir(base^".m")inletsave_xyfhcoord=forp=1toArray2.dim2(pt)dofprintffh"%.13g "(pt.{coord,p})done;fprintffh"\n"inletfh=open_outmatinfprintffh"%% Run in Matlab with: run %s\n\
%% Created by the OCaml Mesh module (version 0.9.0).\n\
%% print -painters -dpdf -r600 %s.pdf\n"matbase;fprintffh"mesh_x = [";save_xyfh1;fprintffh"];\nmesh_y = [";save_xyfh2;fprintffh"];\nmesh_z = [";fori=1toArray1.dim(z)dofprintffh"%.13f "z.{i}done;fprintffh"];\nmesh_triangles = [";fort=1toArray2.dim2(tr)dofprintffh"%i %i %i; "(tr.{1,t})(tr.{2,t})(tr.{3,t})done;letedgecolor=matchedgecolorwith|`None->"'none'"|`Flat->"'flat'"|`Interp->"'interp'"|`Colorc->ifc<0then"'none'"elseletb=float(cland0xFF)/.255.andg=float((clsr8)land0xFF)/.255.andr=float((clsr16)land0xFF)/.255.insprintf"[%g,%g,%g]"rgbinletfacealpha=iffacealpha<0.then0.elseiffacealpha>1.then1.elsefacealphain(* FIXME: protect against strings containing "'". *)fprintffh"];\ntrisurf(mesh_triangles, mesh_x, mesh_y, mesh_z, \
'FaceAlpha', %f, 'EdgeColor', %s, 'LineStyle', '%s');\n"facealphaedgecolorlinestyle;close_outfh;;(* Sort the vertices at node [n0] by increasing (counterclockwise)
angle w.r.t. the base vertex [i0]. [TriangularSurfacePlot] (not
[PlanarGraphPlot] it seems) requires the vertices to be ordered. *)letsort_counterclockwise(pt:mat)n0=function|([]|[_])asadj->adj|n1::tl->letx0=pt.{1,n0}andy0=pt.{2,n0}inletdx1=pt.{1,n1}-.x0anddy1=pt.{2,n1}-.y0in(* Since [atan2] returns an angle in ]-pi, pi], the angle of
(dx1,dy1) will be set to pi so that the order given by the
angles is correct. Also there is no need to norm the vectors
[(dx1,dy1)] and [(dx,dy)] because that will only dilate
[(e1,e2)] which does not change the value of [atan2]. *)letanglen=letdx=pt.{1,n}-.x0anddy=pt.{2,n}-.y0inlete1=-.dx*.dx1-.dy*.dy1ande2=dx*.dy1-.dy*.dx1inatan2e2e1in(* Add angles *)lettl=List.map(funn->(n,anglen))tlinlettl=List.fast_sort(fun(_,a1)(_,a2)->comparea1a2)tlinn1::List.map(fun(n,_)->n)tl;;(* Return an array [adj] such that [adj.(i)] is the list of the
adjacent nodes to [i]. *)letadjacency(mesh:mesh)=letpt=mesh#pointinletn=Array2.dim2(pt)inletadj=Array.make(n+1)[]inletedge=mesh#edgeinfore=1toArray2.dim2(edge)doleti1=edge.{1,e}andi2=edge.{2,e}inadj.(i1)<-i2::adj.(i1);adj.(i2)<-i1::adj.(i2);done;(* This is important for TriangularSurfacePlot (that uses the order
for orientation?). *)Array.mapi(funn0adj->sort_counterclockwiseptn0adj)adjletis_allowed_mathematicac=('0'<=c&&c<='9')||('a'<=c&&c<='z')||('A'<=c&&c<='Z')letcount_mathematica_allowedbase=letn=ref0infori=0toString.lengthbase-1doifis_allowed_mathematica(String.unsafe_getbasei)thenincrndone;!n(* Remove all chars that are not alphanumeric. *)letmathematica_safebase=letlen=count_mathematica_allowedbaseiniflen=String.lengthbasethenbaseelse(letbase'=Bytes.createleninletj=ref0infori=0toString.lengthbase-1doletc=String.unsafe_getbaseiinifis_allowed_mathematicacthen(Bytes.setbase'!jc;incrj;)done;Bytes.unsafe_to_stringbase')letmathematica_print_floatfhf=lets=Bytes.unsafe_of_string(sprintf"%.16g"f)intrylete=Bytes.indexs'e'inoutputfhs0e;output_stringfh"*^";outputfhs(e+1)(Bytes.lengths-e-1)withNot_found->outputfhs0(Bytes.lengths)letmathematica(mesh:mesh)(z:vec)fname=letpt=mesh#pointinifArray2.dim2(pt)=0theninvalid_arg"Mesh.mathematica: mesh#point must be nonempty";ifArray2.dim1(pt)<>2theninvalid_arg"Mesh.mathematica: mesh#point must have 2 rows (fortran)";ifArray2.dim2(mesh#edge)=0theninvalid_arg"Mesh.mathematica: mesh#edge must be nonempty";ifArray2.dim1(mesh#edge)<>2theninvalid_arg"Mesh.mathematica: mesh#edge must have 2 rows (fortran)";letbase=Filename.basenamefnameinletpkg,fname=ifFilename.check_suffixbase".m"thenmathematica_safe(String.subbase0(String.lengthbase-2)),fnameelsemathematica_safebase,fname^".m"inletpkg=String.capitalize_asciipkginletfh=open_outfnameinfprintffh"(* Created by the OCaml Mesh module (version 0.9.0)) \
*)\n";fprintffh"%s`xyz = {"pkg;output_stringfh"{";mathematica_print_floatfhpt.{1,1};output_stringfh", ";mathematica_print_floatfhpt.{2,1};output_stringfh", ";mathematica_print_floatfhz.{1};output_stringfh"}";fori=1+1toArray2.dim2(pt)dooutput_stringfh", {";mathematica_print_floatfhpt.{1,i};output_stringfh", ";mathematica_print_floatfhpt.{2,i};output_stringfh", ";mathematica_print_floatfhz.{i};output_stringfh"}"done;fprintffh"};\n\n";letadj=adjacencymeshinletoutput_adji=(* mathematica indices start at 1 *)matchadj.(i)with|[]->fprintffh"{%i, {}}"(i)|n::tl->fprintffh"{%i, {%i"(i)(n);List.iter(funn->fprintffh", %i"(n))tl;fprintffh"}}"infprintffh"%s`adj = {"pkg;output_adj1;fori=1+1toArray.lengthadj-1dooutput_stringfh", ";output_adjidone;fprintffh"};\n\n";fprintffh"Needs[\"ComputationalGeometry`\"];\n";fprintffh"TriangularSurfacePlot[%s`xyz, %s`adj, Axes -> True]\n"pkgpkg;close_outfh;;(************************************************************************)(* mesh_level_curvesF.ml included by "make_FC_code.ml" with Mesh = "Mesh". *)(* Generic code to draw level cuves. To be included in a file that
defines the drawing primitives. *)moduleM=Map.Make(structtypet=intletcomparexy=compare(x:int)yend)(* Module to build a structure helping to determine when the segment
joining 2 points are on the boundary. *)moduleEdge=structletmake()=refM.emptyletadd_edgeti1i2=assert(i1<i2);tryletv=M.findi1!tinv:=i2::!vwithNot_found->t:=M.addi1(ref[i2])!t(* Declare the segment joining the points of indexes [i1] and [i2]
as being part of the boundary. It is auusmed that [i1 <> i2]. *)letaddti1i2=ifi1<i2thenadd_edgeti1i2elseadd_edgeti2i1leton_boundaryti1i2=assert(i1<i2);tryletv=M.findi1!tinList.memi2!vwithNot_found->false(* Tells whether the segment (if any) joining the points of indices
[i1] and [i2] is on the boundary (according to the information in
[t]). It is assumed that [i1 <> i2]. *)letonti1i2=ifi1<i2thenon_boundaryti1i2elseon_boundaryti2i1end;;letdefault_level_eql1l2=abs_float(l1-.l2)<=1E-8*.(abs_floatl1+.abs_floatl2)letmidpq={x=0.5*.(p.x+.q.x);y=0.5*.(p.y+.q.y)}(* Intersection of the curve et level [l] and the line passing through
(x1,y1) and (x2,y2). [z1 <> z2] assumed. *)letintercept{x=x1;y=y1}z1{x=x2;y=y2}z2l=letd=z1-.z2anda=l-.z2andb=z1-.lin{x=(a*.x1+.b*.x2)/.d;y=(a*.y1+.b*.y2)/.d}letdraw_levels~boundary(mesh:mesh)(z:vec)?(level_eq=default_level_eq)levelssurf=letedge=mesh#edgeinletmarker=mesh#edge_markerinletpt=mesh#pointinifArray2.dim2(edge)=0theninvalid_arg("Mesh.level_curves: mesh#edge must be nonempty");ifArray2.dim1(edge)<>2theninvalid_arg("Mesh.level_curves: mesh#edge must have 2 rows (fortran)");ifArray1.dimmarker<Array2.dim2(edge)theninvalid_arg("Mesh.level_curves: dim mesh#edge_marker < number edges");ifArray2.dim2(pt)=0theninvalid_arg("Mesh.level_curves: mesh#point must be nonempty");ifArray2.dim1(pt)<>2theninvalid_arg("Mesh.level_curves: mesh#point must have 2 rows (fortran)");letbd=Edge.make()in(* Draw the boundary edges *)fore=1toArray2.dim2(edge)doletm=marker.{e}inifm<>0(* not an interior point *)thenbeginleti1=edge.{1,e}andi2=edge.{2,e}inEdge.addbdi1i2;(* collect boundary points *)matchboundarymwith|None->()|Somecolor->letp1={x=pt.{1,i1};y=pt.{2,i1}}andp2={x=pt.{1,i2};y=pt.{2,i2}}inlinesurfcolorp1p2enddone;lettr=mesh#triangleinifArray2.dim2(tr)=0theninvalid_arg("Mesh.level_curves: mesh#triangle must be nonempty");ifArray2.dim1(tr)<3theninvalid_arg("Mesh.level_curves: mesh#triangle must have at least 3 \
rows (fortran) or 3 columns (C)");letmarker=mesh#point_markerinfort=1toArray2.dim2(tr)doleti1=tr.{1,t}andi2=tr.{2,t}andi3=tr.{3,t}inletp1={x=pt.{1,i1};y=pt.{2,i1}}andz1=z.{i1}inletp2={x=pt.{1,i2};y=pt.{2,i2}}andz2=z.{i2}inletp3={x=pt.{1,i3};y=pt.{2,i3}}andz3=z.{i3}inList.iter(fun(l,color)->(* Draw the level curve [l] on the triangle [t] except if
that curve is on the boundary. *)iflevel_eqlz1then(iflevel_eqlz2then(iflevel_eqlz3then(* The entire triangle is at the same level. Try to
remove boundary edges. *)ifEdge.onbdi1i2thenifEdge.onbdi1i3||Edge.onbdi2i3thentrianglesurfcolorp1p2p3(* Full triangle ! *)elselinesurfcolorp3(midp1p2)else(* i1-i2 not on boundary *)ifEdge.onbdi1i3thenifEdge.onbdi2i3thentrianglesurfcolorp1p2p3elselinesurfcolorp2(midp1p3)else(* i1-i3 not on boundary *)ifEdge.onbdi2i3thenlinesurfcolorp1(midp2p3)elsetrianglesurfcolorp1p2p3(* Full triangle ! *)else(* l = z1 = z2 <> z3 *)ifnot(Edge.onbdi1i2)thenlinesurfcolorp1p2)else(* l = z1 <> z2 *)iflevel_eqlz3then(* l = z1 = z3 <> z2 *)(ifnot(Edge.onbdi1i3)thenlinesurfcolorp1p3)elseif(z2<l&&l<z3)||(z3<l&&l<z2)thenlinesurfcolorp1(interceptp2z2p3z3l))elseifl<z1then(iflevel_eqlz2theniflevel_eqlz3then(ifnot(Edge.onbdi2i3)thenlinesurfcolorp2p3)elseifl>z3then(* z3 < l = z2 < z1 *)linesurfcolorp2(interceptp1z1p3z3l)else(* corner point, inside the domain. Ususally this
happens because the level line passes through a
triangle corner. *)(ifmarker.{i2}=0thenpointsurfi2p2)elseifl<z2then(iflevel_eqlz3then(ifmarker.{i3}=0thenpointsurfi3p3)elseifl>z3thenlinesurfcolor(interceptp1z1p3z3l)(interceptp2z2p3z3l))else(* z2 < l < z1 *)linesurfcolor(interceptp1z1p2z2l)(iflevel_eqlz3thenp3elseifl<z3theninterceptp2z2p3z3lelse(* l > z3 *)interceptp1z1p3z3l))else(* l > z1 *)((* Symmetric of [l < z1] with all inequalities reversed *)iflevel_eqlz2theniflevel_eqlz3then(ifnot(Edge.onbdi2i3)thenlinesurfcolorp2p3)elseifl<z3then(* z1 < l = z2 < z3 *)linesurfcolorp2(interceptp1z1p3z3l)else(* corner point, inside the domain *)(ifmarker.{i2}=0thenpointsurfi2p2)elseifl>z2then(iflevel_eqlz3then(ifmarker.{i3}=0thenpointsurfi3p3)elseifl<z3thenlinesurfcolor(interceptp1z1p3z3l)(interceptp2z2p3z3l))else(* z1 < l < z2 *)linesurfcolor(interceptp1z1p2z2l)(iflevel_eqlz3thenp3elseifl>z3theninterceptp2z2p3z3lelse(* l < z3 *)interceptp1z1p3z3l)))levelsdone;;typepolygon_fill=|Tri123(* triangle with edge 1 and cut in edges 2, 3 *)|Tri231|Tri312|Quad123(* Quadrilateral with edges 1-2 and 1-3 of the triangle cut *)|Quad231|Quad312|Whole|Empty;;(* base 3: c1 + 1 + 3(c2 + 1) + 9(c3 + 1). The [c1], [c2] and [c3]
are the comparisons of the 3 corners with the desired level. *)letindexc1c2c3=c1+3*c2+9*c3+13letsuper=letd=Array.make27Emptyind.(index(1)(1)(1))<-Whole;d.(index(1)(1)(0))<-Whole;d.(index(1)(1)(-1))<-Quad312;d.(index(1)(0)(1))<-Whole;d.(index(1)(0)(0))<-Whole;d.(index(1)(0)(-1))<-Tri123;d.(index(1)(-1)(1))<-Quad231;d.(index(1)(-1)(0))<-Tri123;d.(index(1)(-1)(-1))<-Tri123;d.(index(0)(1)(1))<-Whole;d.(index(0)(1)(0))<-Whole;d.(index(0)(1)(-1))<-Tri231;d.(index(0)(0)(1))<-Whole;d.(index(0)(0)(0))<-Empty;(* > 0 required *)d.(index(0)(0)(-1))<-Empty;d.(index(0)(-1)(1))<-Tri312;d.(index(0)(-1)(0))<-Empty;d.(index(0)(-1)(-1))<-Empty;d.(index(-1)(1)(1))<-Quad123;d.(index(-1)(1)(0))<-Tri231;d.(index(-1)(1)(-1))<-Tri231;d.(index(-1)(0)(1))<-Tri312;d.(index(-1)(0)(0))<-Empty;d.(index(-1)(0)(-1))<-Empty;d.(index(-1)(-1)(1))<-Tri312;d.(index(-1)(-1)(0))<-Empty;d.(index(-1)(-1)(-1))<-Empty;dletsub=letd=Array.make27Emptyind.(index(1)(1)(1))<-Empty;d.(index(1)(1)(0))<-Empty;d.(index(1)(1)(-1))<-Tri312;d.(index(1)(0)(1))<-Empty;d.(index(1)(0)(0))<-Empty;d.(index(1)(0)(-1))<-Tri312;d.(index(1)(-1)(1))<-Tri231;d.(index(1)(-1)(0))<-Tri231;d.(index(1)(-1)(-1))<-Quad123;d.(index(0)(1)(1))<-Empty;d.(index(0)(1)(0))<-Empty;d.(index(0)(1)(-1))<-Tri312;d.(index(0)(0)(1))<-Empty;d.(index(0)(0)(0))<-Empty;(* < 0 required *)d.(index(0)(0)(-1))<-Whole;d.(index(0)(-1)(1))<-Tri231;d.(index(0)(-1)(0))<-Whole;d.(index(0)(-1)(-1))<-Whole;d.(index(-1)(1)(1))<-Tri123;d.(index(-1)(1)(0))<-Tri123;d.(index(-1)(1)(-1))<-Quad231;d.(index(-1)(0)(1))<-Tri123;d.(index(-1)(0)(0))<-Whole;d.(index(-1)(0)(-1))<-Whole;d.(index(-1)(-1)(1))<-Quad312;d.(index(-1)(-1)(0))<-Whole;d.(index(-1)(-1)(-1))<-Whole;dletdraw_xxx_leveldecisionname?(boundary=(fun_->Someblack))(mesh:mesh)(z:vec)lcolorsurf=letedge=mesh#edgeinletedge_marker=mesh#edge_markerinletpt=mesh#pointinifArray2.dim2(edge)=0theninvalid_arg("Mesh"^name^": mesh#edge must be nonempty");ifArray2.dim1(edge)<>2theninvalid_arg("Mesh"^name^": mesh#edge must have 2 rows (fortran)");ifArray1.dimedge_marker<Array2.dim2(edge)theninvalid_arg("Mesh"^name^": dim mesh#edge_marker < number edges");ifArray2.dim2(pt)=0theninvalid_arg("Mesh"^name^": mesh#point must be nonempty");ifArray2.dim1(pt)<>2theninvalid_arg("Mesh"^name^": mesh#point must have 2 rows (fortran)");lettr=mesh#triangleinifArray2.dim2(tr)=0theninvalid_arg("Mesh"^name^": mesh#triangle must be nonempty");ifArray2.dim1(tr)<3theninvalid_arg("Mesh"^name^": mesh#triangle must have at least 3 \
rows (fortran) or 3 columns (C)");fort=1toArray2.dim2(tr)doleti1=tr.{1,t}andi2=tr.{2,t}andi3=tr.{3,t}inletp1={x=pt.{1,i1};y=pt.{2,i1}}andz1=z.{i1}inletp2={x=pt.{1,i2};y=pt.{2,i2}}andz2=z.{i2}inletp3={x=pt.{1,i3};y=pt.{2,i3}}andz3=z.{i3}inmatchdecision.(index(comparez1l)(comparez2l)(comparez3l))with|Tri123->fill_trianglesurfcolorp1(interceptp1z1p2z2l)(interceptp1z1p3z3l)|Tri231->fill_trianglesurfcolorp2(interceptp2z2p3z3l)(interceptp2z2p1z1l)|Tri312->fill_trianglesurfcolorp3(interceptp3z3p1z1l)(interceptp3z3p2z2l)|Quad123->fill_quadrilateralsurfcolor(interceptp1z1p2z2l)(interceptp1z1p3z3l)p3p2|Quad231->fill_quadrilateralsurfcolor(interceptp2z2p3z3l)(interceptp2z2p1z1l)p1p3|Quad312->fill_quadrilateralsurfcolor(interceptp3z3p1z1l)(interceptp3z3p2z2l)p2p1|Whole->fill_trianglesurfcolorp1p2p3|Empty->()done;(* Draw the boundary edges (over the filled area) *)fore=1toArray2.dim2(edge)doletm=edge_marker.{e}inifm<>0(* not an interior point *)thenbeginmatchboundarymwith|None->()|Somecolor->leti1=edge.{1,e}andi2=edge.{2,e}inletp1={x=pt.{1,i1};y=pt.{2,i1}}andp2={x=pt.{1,i2};y=pt.{2,i2}}inlinesurfcolorp1p2enddoneletdraw_super_level?boundarymeshzlevelcolorsurf=draw_xxx_levelsuper".super_level"?boundarymeshzlevelcolorsurfletdraw_sub_level?boundarymeshzlevelcolorsurf=draw_xxx_levelsub".sub_level"?boundarymeshzlevelcolorsurf;;(************************************************************************)letlevel_curves?(boundary=(fun_->Someblack))(mesh:mesh)(z:vec)?level_eqlevelsfname=letfh=open_outfnameinletxmin,xmax,ymin,ymax=bounding_boxmeshinlatex_beginfh(xmax-.xmin)(ymax-.ymin)xminymin;draw_levels~boundarymeshz?level_eqlevelsfh;latex_endfh;close_outfhletsuper_level?boundary(mesh:mesh)(z:vec)levelcolorfname=letfh=open_outfnameinletxmin,xmax,ymin,ymax=bounding_boxmeshinlatex_beginfh(xmax-.xmin)(ymax-.ymin)xminymin;draw_super_level?boundarymeshzlevelcolorfh;latex_endfh;close_outfhletsub_level?boundary(mesh:mesh)(z:vec)levelcolorfname=letfh=open_outfnameinletxmin,xmax,ymin,ymax=bounding_boxmeshinlatex_beginfh(xmax-.xmin)(ymax-.ymin)xminymin;draw_sub_level?boundarymeshzlevelcolorfh;latex_endfh;close_outfh(* Determine the number of superdiagonals + 1 main diagonal *)letband_height_P1filter(mesh:mesh)=lettr=mesh#triangleinletkd=ref0inmatchfilterwith|None->fort=1toArray2.dim2(tr)doleti1=tr.{1,t}andi2=tr.{2,t}andi3=tr.{3,t}inkd:=max4!kd(abs(i1-i2))(abs(i2-i3))(abs(i3-i1))done;!kd+1|Somecond->fort=1toArray2.dim2(tr)doleti1=tr.{1,t}andi2=tr.{2,t}andi3=tr.{3,t}inifcondi1then(ifcondi2thenifcondi3thenkd:=max4!kd(abs(i1-i2))(abs(i2-i3))(abs(i3-i1))else(* exlude i3 *)kd:=max2!kd(abs(i2-i1))else(* exclude i2 *)ifcondi3thenkd:=max2!kd(abs(i3-i1)))else(* exclude i1 *)ifcondi2&&condi3thenkd:=max2!kd(abs(i3-i2))done;!kd+1(* Return the index with the lowest nonnegative [deg] (negative
degrees are ignored). Return [-1] if all degrees are < 0. *)letmin_deg(deg:intarray)=leti=ref(-1)inletdegi=ref(max_int)inforj=1toArray.lengthdeg-1doifdeg.(j)>=0&°.(j)<!degithen(i:=j;degi:=deg.(j))done;!i(* sub
***********************************************************************)(* Iterator with indices adapted to the current layout. *)letreciterifi=function|[]->()|x::tl->fix;iterif(succi)tlletiterifl=iterif1lletfilter_columns_shift(m:int_mat)selectshift=letcols=ref[]inletnselected=ref0in(* length of [cols] *)forc=1toArray2.dim2(m)doifselectmcthen(cols:=c::!cols;incrnselected)done;letcols=List.rev!colsinletm'=Array2.create(int)fortran_layout(Array2.dim1(m))(!nselected)initeri(funipi->forj=1toArray2.dim1(m')dom'.{j,i}<-m.{j,pi}-shiftdone)cols;m',!nselected,colsletsub_markers(v:int_vec)ncols=ifArray1.dimv=0thenv(* no markers *)else(letv'=Array1.create(int)fortran_layout(n)initeri(funipi->v'.{i}<-v.{pi})cols;v')letinternal_sub(mesh:mesh)?poslen=letpos=matchposwith|None->1|Somepos->ifpos<1theninvalid_arg"Mesh.sub: pos < 1";posiniflen<=0theninvalid_arg"Mesh.sub: len <= 0";ifpos+len>Array2.dim2(mesh#point)theninvalid_arg"Mesh.sub: len too large";letshift=pos-1inletmax_point_idx=pos+len-1inletsub_pointi=pos<=i&&i<=max_point_idxin(* Points *)letpoint=Array2.sub_rightmesh#pointposleninletpoint_marker=Array1.submesh#point_markerposlenin(* Segments *)letselect2(m:int_mat)i=sub_pointm.{1,i}&&sub_pointm.{2,i}inletnew_seg,n,cols=filter_columns_shiftmesh#segmentselect2shiftinletnew_seg_marker=sub_markersmesh#segment_markerncolsin(* Triangles *)letselect3(m:int_mat)t=sub_pointm.{1,t}&&sub_pointm.{2,t}&&sub_pointm.{3,t}inletnew_tr,n_tr,cols_tr=filter_columns_shiftmesh#triangleselect3shiftin(* Neighbors corresponding to the selected triangles. *)letnew_neighbor=letold_nbh=mesh#neighborinifArray2.dim2(old_nbh)=0thenold_nbhelse(letnbh=Array2.create(int)fortran_layout(3)(n_tr)in(* new neighbor *)lettrans=Array1.create(int)fortran_layout(Array2.dim2(mesh#triangle))in(* old idx → new *)Array1.filltrans(-1);(* default: no corresponding index *)iteri(funipi->trans.{pi}<-i)cols_tr;iteri(funipi->nbh.{1,i}<-trans.{old_nbh.{1,pi}};nbh.{2,i}<-trans.{old_nbh.{2,pi}};nbh.{3,i}<-trans.{old_nbh.{3,pi}};)cols_tr;nbh)in(* Edges *)letnew_edge,n,cols=filter_columns_shiftmesh#edgeselect2shiftinletnew_edge_marker=sub_markersmesh#edge_markerncolsin(make_mesh~point:point~point_marker:point_marker~segment:new_seg~segment_marker:new_seg_marker~hole:mesh#hole(* keep *)~region:mesh#region(* keep *)~triangle:new_tr~neighbor:new_neighbor~edge:new_edge~edge_marker:new_edge_marker,n_tr,cols_tr)letsub(mesh:mesh)?poslen=letm,_,_=internal_submesh?posleninm(* Permutations
***********************************************************************)(** Apply the permutation [perm] to the [mesh]. *)letdo_permute_pointsname(mesh:mesh)(perm:int_vec)(inv_perm:int_vec):mesh=(* Build the new mesh *)letold_pt=mesh#pointinletn=Array2.dim2(old_pt)inifn<>Array1.dimpermtheninvalid_arg(sprintf"%s: dim2 #point = %i <> dim perm = %i"namen(Array1.dimperm));letpt=Array2.create(float64)fortran_layout(2)(n)inletlast_pt_idx=Array2.dim2(pt)infori=1tolast_pt_idxdoletold_i=perm.{i}inpt.{1,i}<-old_pt.{1,old_i};pt.{2,i}<-old_pt.{2,old_i};done;letold_ptm=mesh#point_markerinletptm=Array1.createintlayout(Array1.dimold_ptm)infori=1toArray1.dim(ptm)doptm.{i}<-old_ptm.{perm.{i}}done;letold_seg=mesh#segmentinletseg=Array2.create(int)fortran_layout(2)(Array2.dim2(old_seg))infors=1toArray2.dim2(seg)doleti1=old_seg.{1,s}inifi1<1||i1>last_pt_idxthenfailwith(sprintf"%s: mesh#segment.{%i} = %i not in [%i..%i]"namesi11last_pt_idx);seg.{1,s}<-inv_perm.{i1};leti2=old_seg.{2,s}inifi2<1||i2>last_pt_idxthenfailwith(sprintf"%s: mesh#segment.{%i} = %i not in [%i..%i]"namesi21last_pt_idx);seg.{2,s}<-inv_perm.{i2};done;letold_tr=mesh#triangleinlettr=Array2.create(int)fortran_layout(Array2.dim1(old_tr))(Array2.dim2(old_tr))infort=1toArray2.dim2(tr)doforc=1toArray2.dim1(tr)dotr.{c,t}<-inv_perm.{old_tr.{c,t}}done;done;letold_edge=mesh#edgeinletedge=Array2.create(int)fortran_layout(2)(Array2.dim2(old_edge))infore=1toArray2.dim2(edge)doedge.{1,e}<-inv_perm.{old_edge.{1,e}};edge.{2,e}<-inv_perm.{old_edge.{2,e}};done;make_mesh~point:pt~point_marker:ptm~segment:seg~segment_marker:mesh#segment_marker~hole:mesh#hole~region:mesh#region~triangle:tr~neighbor:mesh#neighbor~edge:edge~edge_marker:mesh#edge_markerletpermute_points_name="Mesh.permute_points"letpermute_points_unsafemeshperm=letn=Array2.dim2(mesh#point)in(* Inverse perm *)letinv_perm=Array1.createintlayoutninfori=1toArray1.dim(perm)doinv_perm.{perm.{i}}<-idone;do_permute_pointspermute_points_namemeshperminv_permletinverse_permname(perm:int_vec)=(* Inverse perm and check that [perm] is indeed a permuation. *)letinv_perm=Array1.createintlayout(Array1.dimperm)inArray1.fillinv_perm(-1);(* never an index *)letlast_el=Array1.dim(perm)infori=1tolast_eldoletpi=perm.{i}inifpi<1||pi>last_eltheninvalid_arg(sprintf"%s: perm.{%i} = %i not in [%i..%i]"nameipi1last_el)elseifinv_perm.{pi}<0theninv_perm.{pi}<-ielseinvalid_arg(sprintf"%s: not a permutation (perm.{%i} = %i = \
perm.{%i})"nameinv_perm.{pi}pii)done;inv_permletpermute_points(mesh:mesh)~invperm=letinv_perm=inverse_permpermute_points_nameperminifinvthendo_permute_pointspermute_points_namemeshinv_permpermelsedo_permute_pointspermute_points_namemeshperminv_permletdo_permute_trianglesname(mesh:mesh)(perm:int_vec)=letold_tr=mesh#triangleinletn=Array2.dim2(old_tr)inifn<>Array1.dimpermtheninvalid_arg(sprintf"%s: dim2 #triangle = %i <> dim perm = %i"namen(Array1.dimperm));lettr=Array2.create(int)fortran_layout(Array2.dim1(old_tr))(n)inletlast_tr_idx=Array2.dim2(tr)infori=1tolast_tr_idxdoforj=1toArray2.dim1(tr)dotr.{j,i}<-old_tr.{j,perm.{i}}donedone;letold_nbh=mesh#neighborinletnbh=ifArray2.dim2(old_nbh)=0thenold_nbhelse(ifArray2.dim1(old_nbh)<>3theninvalid_arg(sprintf"%s: invalid mesh: ROW #neighbor <> 3"name);ifn<>Array2.dim2(old_nbh)theninvalid_arg(sprintf"%s: invalid mesh: COL #neighbor = %i <> \
COL #triangle = %i"name(Array2.dim2(old_nbh))n);letnbh=Array2.create(int)fortran_layout(3)(n)infori=1tolast_tr_idxdoletold_i=perm.{i}innbh.{1,i}<-old_nbh.{1,old_i};nbh.{2,i}<-old_nbh.{2,old_i};nbh.{3,i}<-old_nbh.{3,old_i};done;nbh)inmake_mesh~point:mesh#point~point_marker:mesh#point_marker~segment:mesh#segment~segment_marker:mesh#segment_marker~hole:mesh#hole~region:mesh#region~triangle:tr~neighbor:nbh~edge:mesh#edge~edge_marker:mesh#edge_markerletpermute_triangles_name="Mesh.permute_triangles"letpermute_triangles(mesh:mesh)~invperm=letinv_perm=inverse_permpermute_triangles_nameperminifinvthendo_permute_trianglespermute_triangles_namemeshinv_permelsedo_permute_trianglespermute_triangles_namemeshperm(* Band
***********************************************************************)(* http://ciprian-zavoianu.blogspot.com/2009/01/project-bandwidth-reduction.html
*)letcuthill_mckee~revperm(mesh:mesh):mesh=letn=Array2.dim2(mesh#point)inletperm=matchpermwith|None->Array1.createintlayoutn|Somep->ifArray1.dimp<>ntheninvalid_arg"Mesh.cuthill_mckee: dim perm <> number of points";pinletdeg=Array.make(n+1)0in(* degree of adjacency of each node *)letnbh=Array.make(n+1)[]in(* list of adjacent nodes *)letedge=mesh#edgeinfore=1toArray2.dim2(edge)doleti1=edge.{1,e}andi2=edge.{2,e}innbh.(i1)<-i2::nbh.(i1);deg.(i1)<-deg.(i1)+1;nbh.(i2)<-i1::nbh.(i2);deg.(i2)<-deg.(i2)+1;done;letfree=ref(1)in(* first free position in [perm] *)letq=Queue.create()inletaddnode=perm.{!free}<-node;incrfree;deg.(node)<--1;(* [i] put in the final vec. *)letnbhs=List.filter(funi->deg.(i)>=0)nbh.(node)inletnbhs=List.fast_sort(funi1i2->comparedeg.(i1)deg.(i2))nbhsinList.iter(funi->Queue.addiq)nbhsinletlast_pt=Array1.dim(perm)inwhile!free<=last_ptdoadd(min_degdeg);whilenot(Queue.is_emptyq)doletc=Queue.takeqinifdeg.(c)>=0thenaddcdonedone;ifrevthen(lets=if1=0thenn-1elsen+1in(* FIXME: cond known at compil. *)fori=1ton/2-1+1dolett=perm.{i}inperm.{i}<-perm.{s-i};perm.{s-i}<-t;done);permute_points_unsafemeshperm(* A Generalized GPS Algorithm For Reducing The Bandwidth And Profile
Of A Sparse Matrix, Q. Wang, Y. C. Guo, and X. W. Shi
http://www.jpier.org/PIER/pier90/09.09010512.pdf *)letggps(mesh:mesh)perm:mesh=letn=Array2.dim2(mesh#point)inletperm=matchpermwith|None->Array1.createintlayoutn|Somep->ifArray1.dimp<>ntheninvalid_arg"Mesh.ggps: dim perm <> number of points";pinletdeg=Array.make(n+1)0in(* degree of adjacency of each node *)letedge=mesh#edgeinfore=1toArray2.dim2(edge)doleti1=edge.{1,e}andi2=edge.{2,e}indeg.(i1)<-deg.(i1)+1;deg.(i2)<-deg.(i2)+1;done;let_v=min_degdegin(* FIXME *)permute_points_unsafemeshperm(* Local Variables: *)(* compile-command: "make -k -C .." *)(* End: *)