New URL for NEMO forge!   http://forge.nemo-ocean.eu

Since March 2022 along with NEMO 4.2 release, the code development moved to a self-hosted GitLab.
This present forge is now archived and remained online for history.
partition.sh in branches/2014/dev_r4840_CMCC2_mapping/NEMOGCM/TOOLS/MPP_PREP – NEMO

source: branches/2014/dev_r4840_CMCC2_mapping/NEMOGCM/TOOLS/MPP_PREP/partition.sh @ 4842

Last change on this file since 4842 was 4842, checked in by epico, 10 years ago

added the bash script for mapping processes to computing nodes

  • Property svn:executable set to *
File size: 20.0 KB
Line 
1#!/bin/bash
2
3# ---------------------------------------------------------------------
4#
5#                      process's mapping
6#                      ***********************
7#
8#   PURPOSE :
9#   ---------
10#             The execution time si also influenced by the internode
11#             communications and by the memory contention between parallel MPI tasks.
12#             This program aims at creating a suitable map of the MPI processes
13#             to the computing NODE according to different mapping strategies.
14#             It acts only on the local scheduler configuration and does not modify
15#             the parallel algorithm neither the domain decomposition.
16#             The following strategies have been implemented:
17
18#                - com:  the mapping will reduce the internode communications. The
19#                        communication pattern defines a graph where each vertex
20#                        represents an MPI task and an edge is a communication between
21#                        two tasks. The mapping is obtained by partitioning the graph
22#                        in a number of subgraph equal to the number of computing nodes;
23#                        the partitioning minimizes the total number of cutting edges.
24
25#                - mem:  the mapping will reduce the memory contention between processes
26#                        by evenly distributing the total amount of allocated memory
27#                        among the computing nodes. The amount of memory allocated in a
28#                        computing node is equal to the memory allocated in the other
29#                        nodes.
30
31#                - away: a process will not be mapped to the same node of any of
32#                        its neighbours
33
34#   INPUT :
35#   -------------
36
37#        This script requires the following external package:
38#         - scotch library: http://www.labri.fr/perso/pelegrin/scotch/ (for the com mapping)
39#         - the file "background.png" (that can be extracted from the bathimetry) to visualize the mapping
40         
41#        npart: number of partitions i.e. number of nodes 
42         
43#        processes layout: the file reports how the MPI processes are disposed on the
44#                          parallel subdomains. This file is an output of the
45#                          "cmcc_mppopt_showproc" script (matrix.in)
46                           
47#        processes coordinates: the file reports the cartesian coordinates of the
48#                               processes subdomain. It is used for the visualization.
49#                               The file is an output of the
50#                               "cmcc_mppopt_showproc" script ( typical name: <confname>-<decomposition>_<num_sub_domain> example: nemo025-019x045_672 )
51
52#        number of ocean points: this file reports for each subdomain the number of ocean points
53#                                and an estimation of the amount of memory required for
54#                                each subdomain. The file is an output of the
55#                                "cmcc_mpp_optimizer" script with extension *.layout
56                 
57
58#    MODIFICATIONS:
59#    --------------
60#        v1.0: Nov 2014 (I.Epicoco, F.Macchia - CMCC)
61# ----------------------------------------------------------------------
62
63
64#Warning !!!
65# check $procxnode variable that must be set to the number of cores per node.
66# It also represents the cardinality of each partition
67
68procxnode=16
69
70
71echo "Usage: partition.sh <proc layout file> <num partitions> <coordinates file> <num ocean point file> <type_of_mapping: {mem, comm, away}>"
72
73matrix=$1
74#matrix="matrix.in"
75
76#check for the processes layout file
77if [ ! -f $matrix ]; then
78  echo "file with process layout not found"
79  exit 1
80fi
81
82maptype=$5
83if [ -z $maptype ]; then
84  maptype="comm"
85fi
86npart=$2
87#npart=21
88
89gridfile=$3
90#gridfile="./nemo116-019x045_672"
91
92#check the process coordinate file
93if [ ! -f $gridfile ]; then
94  echo "processes coordinates file not found"
95  exit 1
96fi
97
98layoutfile=$4
99#layoutfile="./0672_CMCC.layout"
100
101#check the .layout
102if [ ! -f $layoutfile ]; then
103  echo "num ocean points file not found"
104  exit 1
105fi
106
107#output file for the for the mapping
108parmetisfile=$maptype.mapping.$npart
109
110#output file with the TASK GEOMETRY definition
111geometry_var=$maptype.geometry.$npart
112
113#output file for BG/Q settings
114bg_geometry=$maptype.bggeometry.$npart
115
116#Extract the number of rows and columns of the domain decomposition
117nline=$(cat $matrix | wc -l) 
118echo "rows= $nline"
119ncol=$(($(cat $matrix | wc -w)/$nline))
120echo "columns= $ncol"
121
122dim=$((procxnode * npart))
123decomp="$ncol x $nline" #Be aware of the white spaces
124#decomp="8 x 37" #be aware of the white spaces
125
126#############################################################
127# mapping based on cardinal order
128#############################################################
129
130if [ $maptype = "card" ]; then
131
132rm $parmetisfile >& /dev/null
133
134for i in `seq 0 $((npart - 1))`; do
135   for j in `seq 1 $procxnode`; do
136      echo $i >> $parmetisfile
137   done
138done
139fi
140
141#############################################################
142# mapping based on calc2 strategy
143#############################################################
144
145if [ $maptype = "calc2" ]; then
146
147   grep -A $((dim+1)) "$decomp" $layoutfile | grep -v Total | awk '{print $2 " - " $1}' | sort -n -r > oceanpt.def
148   rm mapping.tmp >& /dev/null
149   for i in `seq 0 $((dim-1))`; do
150      line=`sed -n "$((i+1)),$((i+1)) p" oceanpt.def`
151      echo "$((i % npart)) - $line" >> mapping.tmp
152   done
153   
154   cat mapping.tmp | awk '{print $5 " " $1}' | sort -n | cut -d " " -f 2 > $parmetisfile
155
156fi
157
158#############################################################
159# mapping based on memory
160#############################################################
161if [ $maptype = "calc" -o $maptype = "mem" ]; then
162
163rm oceanpt.* >& /dev/null
164
165#reading the ocean points memory usage
166if [ $maptype = "calc" ]; then
167   grep -A $((dim+1)) "$decomp" $layoutfile | grep -v Total | awk '{print $2 " - " $1}' | sort -n -r > oceanpt.def
168else
169   grep -A $((dim+1)) "$decomp" $layoutfile | grep -v Total | awk '{print $3 " - " $1}' | sort -n -r > oceanpt.def
170fi
171
172i=0
173for node in `cat oceanpt.def | cut -d "-" -f 1 | sed -e "s/\.//" -e "s/^0*//"`; do
174   ocnpnt[i]=$node
175   ((i++))
176done;
177
178i=0
179for node in `cat oceanpt.def | cut -d "-" -f 2`; do
180        rank[i]=$node
181        ((i++))
182done;
183
184rm weight* mapp_* >& /dev/null
185
186
187for i in `seq 0 $(($npart-1))`; do
188   echo "0 - $i" >> weight
189done
190
191for i in `seq 0 $(($dim - 1))`; do
192   minrow="`sort -n weight| head -1`"
193   minocn=`echo $minrow | cut -d "-" -f 1 | sed -e "s/ //g"`
194   minnode=`echo $minrow | cut -d "-" -f 2 | sed -e "s/ //g"`
195
196   printf "\r Processing node $((i+1)) of $dim \t"
197
198   echo "${rank[$i]}" >> mapp_$minnode
199   newvalue=$(($minocn + ${ocnpnt[$i]}))
200   if [ `wc -l mapp_$minnode | cut -d " " -f 1` -eq $procxnode ]; then
201       grep -v "${minrow}$" weight > weight.new
202       echo "$newvalue - $minnode" >> weight.final
203   else
204       sed -e "s/${minrow}$/$newvalue - $minnode/" weight > weight.new
205   fi
206   mv weight.new weight
207done
208echo " "
209
210for i in `ls mapp_*`; do
211   idx=`echo $i|cut -d "_" -f 2`
212   sed -e "s/^\(.*\)/\1 $idx/" $i >> metis.part.memory.temp
213done
214
215rm $parmetisfile >& /dev/null
216
217sort -n metis.part.memory.temp | cut -d " " -f 2 >> $parmetisfile
218rm metis.part.memory.temp
219
220rm oceanpt.* weight* mapp_* >& /dev/null
221
222fi
223
224#############################################################
225# Creation of the graph file in parmetis format
226#############################################################
227
228
229el=0
230#for i in `tail -r $matrix`; do
231for i in `tac $matrix`; do
232   m[el]=$(($i+1))
233   ((el++))
234done;
235
236
237if [ $el -ne $(($nline*$ncol)) ] ; then
238   echo "ERROR"
239   exit 1
240fi
241
242for((i=0;i<${#m[@]};i++)) ; do
243   if [ ${m[$i]} -ne 0 ] ; then
244     
245      up=${m[(($i+$ncol))]} ; if [[ $up -ne 0 ]] ; then echo -n "$up   " >> $outdir"parmetis.part"; fi
246     
247      if [ $((i - ncol )) -ge 0 ]; then
248         down=${m[(($i-$ncol))]} ; if [[ $down -ne 0 ]] ; then echo -n "$down   " >> $outdir"parmetis.part"; fi
249      fi
250     
251      #left boundary
252      if [[ $(($i%$ncol)) -eq 0 ]] ; then 
253         left=${m[(($i+$ncol-1))]}
254      else
255         left=${m[(($i-1))]}
256      fi
257      if [[ $left -ne 0 ]] ; then echo -n "$left   " >> $outdir"parmetis.part"; fi 
258     
259      #right boundary
260      if [[ $(($(($i+1))%$ncol)) -eq 0 ]] ; then
261         right=${m[(($i-$ncol+1))]}
262      else
263         right=${m[(($i+1))]} ;
264      fi
265      if [[ $right -ne 0 ]] ; then echo -n "$right   " >> $outdir"parmetis.part"; fi 
266     
267      #north pole domains
268      if [[ $i -ge $(($nline*$ncol-$ncol)) ]] ; then
269         near1=${m[(($ncol*((2*$nline-1))-$i-2))]} ; if [[ ( $near1 != 0 ) && ( $near1 != $up ) && ( $near1 != $down ) && ( $near1 != $left ) && ( $near1 != $right ) && ( $near1 != ${m[$i]} ) ]] ; then echo -n "$near1   " >> $outdir"parmetis.part"; fi
270         near2=${m[(($ncol*((2*$nline-1))-$i-1))]} ; if [[ $near2 -ne 0  && ( $near2 != $up ) && ( $near2 != $down ) && ( $near2 != $left ) && ( $near2 != $right ) && ( $near2 != $near1 ) && ( $near2 != ${m[$i]} ) ]] ; then echo -n "$near2   " >> $outdir"parmetis.part"; fi
271         near3=${m[(($ncol*((2*$nline-1))-$i))]} ; if [[ $near3 -ne 0 && ( $near3 != $up ) && ( $near3 != $down ) && ( $near3 != $left ) && ( $near3 != $right ) && ( $near3 != $near1 ) && ( $near3 != $near2) && ( $near3 != ${m[$i]} ) ]] ; then echo -n "$near3   " >> $outdir"parmetis.part"; fi
272      fi
273     
274      echo "" >> $outdir"parmetis.part"
275   fi
276done;
277
278node=$(cat $outdir"parmetis.part" | wc -l)
279edge=$(cat $outdir"parmetis.part" | wc -w)
280edge=$(($edge/2))
281
282echo "$node $edge"> $outdir"parmetis.in"
283while read line; do
284   echo "${line}" >> $outdir"parmetis.in"
285done < $outdir"parmetis.part"
286
287rm $outdir"parmetis.part"
288
289#############################################################
290# mapping based on communication
291#############################################################
292
293if [ $maptype = "comm" ]; then
294#execution of scotch (gpart num_part [input graph file] [output map file] -b0)
295
296#translate the parmetis format file into the scotch format file
297gcv parmetis.in scotch.in -ic -os
298
299#partitioning with scotch
300gpart $npart scotch.in scotch.map -b0
301
302#translate the output file format
303sed -n "s/\t/ /p" scotch.map | cut -d " " -f 2 > $parmetisfile
304
305rm scotch.map scotch.in
306
307#lancia metis (gpmetis [options] graphfile nparts)
308
309#gpmetis -ptype=kway -objtype=vol parmetis.in $npart
310#gpmetis -contig -ufactor=1 -ncuts=50 -niter=200 -ptype=kway -objtype=cut parmetis.in $npart
311
312fi # end maptype==comm
313
314
315#############################################################
316# Mapping based on neighbour-away algorithm
317#############################################################
318
319if [ $maptype = "away" ]; then
320
321# the script partitions the input nodes of the graph into "npart" sets. One node is requested to belong
322# to a partition different from each of its neighbours
323
324#input file describing the dependency graph. Format used is ParMetis adiacency list file
325graphfile="parmetis.in"
326
327# create npart temporary files
328for i in `seq 0 $((npart - 1))`; do
329        p_filename=`printf "p_%04d" $i`
330        rm $p_filename >& /dev/null
331        touch $p_filename
332done
333num_nodes=`sed -n "1,1 s/[0-9]*$//p" $graphfile`
334for i in `seq 1 $num_nodes`; do
335        # processing node $i
336        printf "\r Processing node $i of $num_nodes \t"
337        # sort the current partitions
338        ordered_file="p_ordered"
339        wc -l p_*[0-9] | sed -n "1,$npart s/^ *//p" | sort -n > $ordered_file
340
341        # insert the current node $i into a partition where none of its neighbours is present
342        neighbour_list=`sed -n "$((i+1)),$((i+1)) p" $graphfile`
343        placed=0
344        for j in `seq 1 $npart`; do
345                p_filename=`sed -n "$j,$j p" $ordered_file | cut -d " " -f 2`
346                insert=1
347                for k in $neighbour_list; do
348                        flag=`grep "^$k$" $p_filename | wc -l`
349                        if [ $flag != 0 ]; then
350                                insert=0
351                                break
352                        fi
353                done
354                if [ $insert == 0 ]; then
355                        continue
356                fi
357                echo $i >> $p_filename
358                placed=1
359                break
360        done
361        if [ $placed == 0 ]; then
362                echo "Error node $i can not be placed in any of the partitions"
363                exit 1
364        fi
365done
366printf "\n"
367
368rm $ordered_file
369
370# merging of the entire temporary partions files
371rm $parmetisfile >& /dev/null
372for i in `seq 1 $num_nodes`; do
373        part=`grep "^$i$" p_*[0-9] | cut -d "_" -f 2 | cut -d ":" -f 1 | sed "s/^0*//"`
374        if [ x$part == "x" ]; then
375                part=0
376        fi
377        printf "%d\n" $part >> $parmetisfile
378done
379
380rm p_*[0-9]
381
382fi # end maptype = "away"
383
384#############################################################
385# Check for correctness
386#############################################################
387
388#check if each partition has the same number of elements
389for i in `eval echo {0..$(($npart-1))}`; do
390   n=`grep "^$i$" $parmetisfile | wc -l`
391   if [ $n -ne $procxnode ] ; then
392      echo "ERROR! Node $i has $n proc!"
393   fi
394done
395
396#############################################################
397# Diagnostic
398#############################################################
399
400# Memory needed for each partitions
401echo "*******************"
402echo "Memory Distribution"
403echo "*******************"
404grep -A $((dim+1)) "$decomp" $layoutfile | grep -v Total | awk '{print $3}' > oceanpt.tmp
405
406for i in `seq 1 $dim`; do
407   p=`sed -n "$i,$i p" $parmetisfile`
408   weight=`sed -n "$i,$i p" oceanpt.tmp`
409   w[p]=`echo 0${w[$p]} + $weight | bc`
410done
411
412rm oceanpt.tmp
413
414max=${w[0]}
415min=${w[0]}
416tot=${w[0]}
417min_p=0
418max_p=0
419echo "0 - ${w[0]} GB"
420for i in `seq 1 $((npart-1))`; do
421   echo "$i - ${w[$i]} GB"
422   t=`echo "${w[$i]} > $max" | bc`
423   if [ $t == 1 ]; then max=${w[$i]}; max_p=$i; fi
424   t=`echo "${w[$i]} < $min" | bc`
425   if [ $t == 1 ]; then min=${w[$i]}; min_p=$i; fi
426   tot=`echo "$tot + ${w[$i]}" | bc`
427done
428mean=`echo "scale = 3; $tot/$npart"|bc `
429echo "Max memory  $max GB on partition $max_p"
430echo "Min memory  $min GB on partition $min_p"
431echo "Mean memory $mean GB"
432echo "Memory unbalance (Max/Mean)" `echo "scale = 2; $max/$mean" |bc` "%" 
433
434# Number of interconnection (intra- and inter- node) between processes
435echo "*********************************"
436echo "Interconnection between processes"
437echo "*********************************"
438
439com=0
440comtot=0
441
442i=1
443while read p; do
444   part[i]=$p
445   ((i++))
446done < $parmetisfile
447
448l=0
449while read line; do
450   if [[ l -ne 0 ]] ; then
451      for i in `echo $line`; do
452         if [[ ${part[$i]} -ne ${part[$l]} ]] ; then
453            ((com++))
454         fi
455         ((comtot++))
456      done
457   fi   
458   ((l++))
459done < $outdir"parmetis.in"
460
461echo "Tot comunicazioni: $comtot"
462echo "Tot comunicazioni internodo: $com"
463
464
465#############################################################
466#writing the TASK_GEOMETRY
467#############################################################
468
469number_of_groups=`cat $parmetisfile | sort -u | wc -l`
470
471echo -n 'export LSB_PJL_TASK_GEOMETRY="{' > $geometry_var
472#for j in `seq 0 $((number_of_groups - 1))`; do
473for j in `eval echo {0..$((number_of_groups - 1))}`; do
474 echo -n "(" >> $geometry_var
475   for i in `grep -n "^$j$" $parmetisfile | cut -d : -f 1`; do
476     echo -n $((i-1)),
477   done | sed s/.$// >> $geometry_var
478echo -n ")" >> $geometry_var
479done;
480echo '}"' >> $geometry_var
481
482#cat $geometry_var
483
484
485#############################################################
486#writing the geometry for BG/Q architecture
487#############################################################
488
489# each row contains the position of the process in the torus
490# the shape of the torus is defined by the 6 parameters A, B, C, D, E, T
491
492tor_a=2
493tor_b=2
494tor_c=4
495tor_d=4
496tor_e=2
497tor_t=16
498
499cat -n $parmetisfile | awk '{print $2 " " $1}'| sort -n | cat -n | awk -v val=$tor_t '{print $3 " " $2 " " ($1-1) % val}' | sort -n | awk -v val_a=$tor_a -v val_b=$tor_b -v val_c=$tor_c -v val_d=$tor_d -v val_e=$val_e '{print int($2/(val_b*val_c*val_d*val_e)) " " int($2/(val_c*val_d*val_e)) % val_b " " int($2/(val_d*val_e)) % val_c " " int($2/val_e) % val_d " " $2 % val_e  " " $3}' > $bg_geometry
500
501
502#############################################################
503#writing svg
504#############################################################
505#load the mapping file
506
507i=1
508for node in `cat $parmetisfile`; do
509   map[i]=$node
510   ((i++))
511done;
512
513#loading of the number of ocean points and memory usage
514decomp=`grep "Decomp" $gridfile | cut -d : -f 3`
515k=0
516for oceanpt in `grep -A ${#map[*]} "$decomp" $layoutfile | grep -v Total | awk '{print $2}'` ; do
517   oceanptvett[k]=$oceanpt
518   ((k++))
519done;
520k=0
521for mempt in `grep -A ${#map[*]} "$decomp" $layoutfile | grep -v Total | awk '{print $3}'` ; do
522   memptvett[k]=$mempt
523   ((k++))
524done;
525
526#creation of the color map
527
528num_col=$npart
529for((i=0;i<$num_col;i++))  ; do
530   colourvett[i]="rgb($(($RANDOM%256)),$(($RANDOM%256)),$(($RANDOM%256)))"
531done;
532
533outfile=$maptype.display.$npart.svg
534
535#extract the grid dimension
536width=0 ; height=0;
537for i in `grep -A4 "# [ 0-9]" $gridfile | grep -v \- | grep -v \# | sed "s/^[ \t]*//;s/[ \t]*$//" | sed "s/  */ /g" |sed "s/ /,/g" | cut -f 1 -d ,`; do
538   if [[ $i -gt $width ]]; then
539      width=$i
540   fi
541done;
542for i in `grep -A4 "# [ 0-9]" $gridfile | grep -v \- | grep -v \# | sed "s/^[ \t]*//;s/[ \t]*$//" | sed "s/  */ /g" |sed "s/ /,/g" | cut -f 2 -d ,`; do
543   if [[ $i -gt $height ]]; then
544      height=$i
545   fi
546done;
547
548#writing the header
549echo "<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?> " > $outfile
550echo "<!DOCTYPE svg PUBLIC \"-//W3C//Dtd SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/Dtd/svg11.dtd\">" >> $outfile 
551echo "<svg width=\"$(($width+150))\" height=\"$(($height+100))\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">" >> $outfile
552
553#background image. It can be derived from the bathimetry
554echo "<image x=\"0\" y=\"0\" width=\"$width\" height=\"$height\" xlink:href=\"background.png\"/>" >> $outfile
555
556#writing the subdomain rectangles with additional information about process ID and color
557j=0;
558for i in `grep -A4 "# [ 0-9]" $gridfile | grep -v \- | sed "s/#/ /g" | sed "s/^[ \t]*//;s/[ \t]*$//" | sed "s/  */ /g" |sed "s/ /,/g" | sed '1d'`; do
559   case $j in
560   0)
561      proc=$(($i-1));
562      nodeid=node_$proc
563      col_index=${map[$i]}
564      echo -n "<polygon id=\"$nodeid\" points=\"" >> $outfile
565      ((j++));
566                printf "\rGenerating SVG ... %3d%%  " $(( (($proc + 1 ) * 100) / $dim ))
567   ;;
568   1)
569      ((j++));
570      x=$(echo $i | cut -f 1 -d ,);
571      y=$(($height-$(echo $i | cut -f 2 -d ,)));
572      xtxt=$(($x+3))
573      ytxt=$(($y-3))
574      echo -n "$x,$y "  >> $outfile
575   ;;
576   [2-3])
577      ((j++));
578      x=$(echo $i | cut -f 1 -d ,);
579      y=$(($height-$(echo $i | cut -f 2 -d ,)));
580      echo -n "$x,$y "  >> $outfile
581   ;;
582   4)
583      j=0;
584      x=$(echo $i | cut -f 1 -d ,);
585      y=$(($height-$(echo $i | cut -f 2 -d ,)));
586      echo "$x,$y\" style=\"stroke:white;fill:${colourvett[$col_index]};fill-opacity:0.7\" />" >> $outfile
587      echo "<text x=\"$xtxt\" y=\"$ytxt\" fill=\"white\" >$proc</text>" >> $outfile
588      echo "<g visibility=\"hidden\" font-family =\"sans-serif\" fill=\"black\" font-size = \"12\">" >> $outfile
589      echo "<rect x=\"$xtxt\" y=\"$ytxt\" rx=\"2\" ry=\"2\" width=\"175\" height=\"95\" style=\"stroke:black;fill:lightgray\">" >> $outfile
590      echo "<set attributeName=\"visibility\" from=\"hidden\" to=\"visible\" begin=\"$nodeid.mousedown\" end=\"$nodeid.mouseout\"/></rect>" >> $outfile
591      echo "<text x=\"$(($xtxt+10))\" y=\"$(($ytxt+20))\" font-weight=\"bold\">Ocean Sub-domain : $proc<set attributeName=\"visibility\" from=\"hidden\" to=\"visible\" begin=\"$nodeid.mousedown\" end=\"$nodeid.mouseout\"/></text> " >> $outfile
592      echo "<text x=\"$(($xtxt+10))\" y=\"$(($ytxt+40))\">Total ocepts : ${oceanptvett[$proc]}<set attributeName=\"visibility\" from=\"hidden\" to=\"visible\" begin=\"$nodeid.mousedown\" end=\"$nodeid.mouseout\"/></text> " >> $outfile
593      echo "<text x=\"$(($xtxt+10))\" y=\"$(($ytxt+60))\">Memory in Gb : ${memptvett[$proc]}<set attributeName=\"visibility\" from=\"hidden\" to=\"visible\" begin=\"$nodeid.mousedown\" end=\"$nodeid.mouseout\"/></text> " >> $outfile
594      echo "<text x=\"$(($xtxt+10))\" y=\"$(($ytxt+80))\">Color : $col_index<set attributeName=\"visibility\" from=\"hidden\" to=\"visible\" begin=\"$nodeid.mousedown\" end=\"$nodeid.mouseout\"/></text> " >> $outfile
595      echo "</g>" >> $outfile
596   ;;
597   *) 
598      echo "ERROR"
599   ;;
600   esac;
601done;
602
603#chiudo il file svg
604echo "</svg>" >> $outfile
605 
606printf " Done\n"
Note: See TracBrowser for help on using the repository browser.