xmg.pl

xmg.pl hybrid perl/xmgrace language for generating plots from a script.

I like gnuplot, since it can be driven by simple scripts. However, the plots are not as professional as xmgrace, and the scripts rather simple compared to say what you can do with Perl. xmg.pl provides the solution to both problems, as it creates a hybrid language between xmgrace batch commands and perl, and feeds these to xmgrace through a named pipe.

Calling script

#!/bin/sh
eval 'exec perl /home/zqex/bin/xmg.pl $0 ${1+"$@"}'
:

This makes sure any commandline arguments to the script are available in the @ARGV array. You to should change /home/zqex/bin to the location of the script. (remember to "chmod +x xmg.pl", and check if #!/usr/bin/perl is correct for your installation)

Pure xmgrace batchlanguage

#!/bin/sh
eval 'exec perl /home/zqex/bin/xmg.pl $0 ${1+"$@"}'

#xaxis label "t"
#xaxis label "r\S2\N(t)"

#read xy "data"
#s_ symbol 1
#s_ line type 0
#s_ symbol color "red"

#read xy "theory"
#s_ symbol 0
#s_ line type 1
#s_ line color "black"

All lines starting with '#' are xmgrace batch language. s_ is a shorthand form to refer to the last read dataset. Look at the xmgrace manual for more information. An easy way to find all possible commands, is to save a project in xmgrace, and look at the file. The script loads one file 'data' using red circles, and another 'theory' as a black line.

Hybrid perl/xmgrace

#!/bin/sh
eval 'exec perl /home/zqex/bin/xmg.pl $0 ${1+"$@"}'

#xaxis label "t"
#xaxis label "r\S2\N(t)"

$sym=1;
$yshift=0;
foreach (sort glob "data/*")
{
#read xy "$_"
#s_.y=s_.y+$yshift
#s_ legend "$_"
#s_ symbol $sym
#s_ line type 0
$sym++;
$yshift++;
}

The glob finds all files in the data directory sorted alphabetically, and loads them with different symbol types, and sets the legend for each file. Each file is shifted +1 along the yaxis relative to the last file.

Is is often sensible to define perl functions that takes the filename and handles loading the file, a practical way to set symbols and colours is by evaluating a regex on the filename.

Generating plot data in Perl

#!/bin/sh
eval 'exec perl /home/zqex/bin/xmg.pl $0 ${1+"$@"}'

sub AddTheory
{
  $data=shift;

  $qmin=1;
  $qmax=10000;

  for ($i=0;$i<$N;$i++)
    {
##log distributed datapoints    
      $x=10**($i/$N*log10($qmax/$qmin)+log10($qmin));
      $y=10^($x*$data);

#g$graph.s$set point $x,$y
    }

#g$graph.s$set symbol 0
#g$graph.s$set line type 1
#g$graph.s$set line linestyle $ls
#g$graph.s$set line linewidth $lw
#g$graph.s$set line color 1

  $set++;
}

$graph=0; $set=0;

##line style
$ls=2;

##linewidth
$lw=3;

#add theory lines to graph 0
AddTheory(1);
AddTheory(2);
AddTheory(3);
AddTheory(4);

Notice graph/set has to be handled manually. Secondly that all lines with '##' are regarded as comments.

Sending commandline arguments to xmgrace

#!/bin/sh
eval 'exec perl /home/zqex/bin/xmg.pl $0 ${1+"$@"}'
##xmgrace -geometry 1320x960 -autoscale none
:

'##xmgrace' is a special remark, that tells xmg.pl that the rest of the line should be used as argument to the xmgrace instance started by xmg.pl.

Debugging

When running a script, what happens is that xmg.pl is called with the script as first argument, followed by any arguments on the commandline.

xmg.pl loads the script line by line. Lines with '#' are converted into print statements, and the result is a valid perl program. That program is the evaluated, and the output is send through a named pipe to xmgrace.

xmg.pl myscript code <args>

will print the pure perl code generated by processing myscript.

xmg.pl myscript eval <args>

will print the output from evaluating the perl program obtained from myscript.

A very long example

#!/bin/sh
eval 'exec perl /home/zqex/bin/xmg.pl $0 ${1+"$@"}'
##xmgrace -geometry 1320x960 -autoscale none

use lib '/home/zqex/include';

## load an external perl library, where I have implemented the theory in C
use ExtUtils::testlib;
use DoubleTube;


## A4 and filename
#PAGE SIZE 3000,400


##filename of when printing
#PRINT TO "rg_abc.eps"

$b=0.7528; $b2=$b*$b;

$dC_rnd=2.77*$b;
$Phi_rnd=0.912;

$dC_end=2.57*$b;
$Phi_end=0.916;


my $Rg=0;
my $Nlab=0;
my $l;

sub LoadFileRg
{
  $fn=shift;

  ($lam)= $fn =~ /s(\d\d\d)/;
  if ($fn =~ /x$/) {$l=$lam/100;}
  if ($fn =~ /y$/) {$l=1.0/sqrt($lam/100);}

  $simsym=1;
  if ($fn=~/s200.x/ ) { $simsym=8; }
  if ($fn=~/s200.y/ ) { $simsym=9; }
  if ($fn=~/s400.x/ ) { $simsym=2; }
  if ($fn=~/s400.y/ ) { $simsym=3; }

  open FI,"$fn";
  while ()
     {
        ($x,$y)=split ;

#g$graph.s$set point $x,$y
     }
  close(FI);

#g$graph.s$set symbol $simsym
#g$graph.s$set symbol size 1.5
#g$graph.s$set symbol linewidth 3.0
#g$graph.s$set line type 0
#g$graph.s$set symbol color 1
#g$graph.s$set comment "$fn"
$set++;
}


sub LoadFileRg2
{
  $fn=shift;

  ($lam)= $fn =~ /s(\d\d\d)/;
  if ($fn =~ /x$/) {$l=$lam/100;}
  if ($fn =~ /y$/) {$l=1.0/sqrt($lam/100);}

  $simsym=1;
  if ($fn=~/s200.x/ ) { $simsym=8; }
  if ($fn=~/s200.y/ ) { $simsym=9; }
  if ($fn=~/s400.x/ ) { $simsym=2; }
  if ($fn=~/s400.y/ ) { $simsym=3; }

  open FI,"$fn";
  while ()
     {
        ($x,$y)=split ;
        $y/=(($x-1)*$b2)/6.0;

#g$graph.s$set point $x,$y
     }
  close(FI);

#g$graph.s$set symbol $simsym
#g$graph.s$set symbol size 1.5
#g$graph.s$set symbol linewidth 3.0
#g$graph.s$set line type 0
#g$graph.s$set symbol color 1
#g$graph.s$set comment "$fn"
$set++;
}

sub AddTheoryRg
{
  $N=200;
  $qmin=1;
  $qmax=10000;

  for ($i=0;$i<$N;$i++)
    {
      $x=10**($i/$N*log10($qmax/$qmin)+log10($qmin));

## comes from a c library
      $y=DoubleTube::RadiusGyration2($b,$x,$dC,$Phi,$l)/
         DoubleTube::RadiusGyration2($b,$x,$dC,$Phi,1.0);

#g$graph.s$set point $x,$y
    }

#g$graph.s$set symbol 0
#g$graph.s$set line type 1
#g$graph.s$set line linestyle $ls
#g$graph.s$set line linewidth $lw

#g$graph.s$set line color 1
#g$graph.s$set comment "theory $fn"

  $set++;
}


sub AddTheoryRee
{
  $N=200;
  $qmin=0.1;
  $qmax=5000;

  for ($i=0;$i<$N;$i++)
    {
      $x=10**($i/$N*log10($qmax/$qmin)+log10($qmin));

      $y=DoubleTube::RxxC($b,$dC,$Phi,$x,$l)/
         DoubleTube::RxxC($b,$dC,$Phi,$x,1.0);

#g$graph.s$set point $x,$y
    }

#g$graph.s$set symbol 0
#g$graph.s$set line type 1
#g$graph.s$set line linestyle $ls
#g$graph.s$set line linewidth $lw

#g$graph.s$set line color 1
#g$graph.s$set comment "theory $fn"

  $set++;
}

sub LoadFileRee
{
  $fn=shift;

  ($lam)= $fn =~ /s(\d\d\d)/;
  if ($fn =~ /x$/) {$l=$lam/100;}
  if ($fn =~ /y$/) {$l=1.0/sqrt($lam/100);}

  $simsym=1;
  if ($fn=~/s200/ and $fn=~/x$/ ) { $simsym=8; }
  if ($fn=~/s200/ and $fn=~/y$/ ) { $simsym=9; }
  if ($fn=~/s400/ and $fn=~/x$/ ) { $simsym=2; }
  if ($fn=~/s400/ and $fn=~/y$/ ) { $simsym=3; }

  $xold=0;
  open FI,"$fn";
  while ()
     {
        ($n,$x)=split ;
        next unless &$filter($n,$xold);

#g$graph.s$set point $n,$x
        $xold=$n;
     }
  close(FI);

#g$graph.s$set symbol $simsym
#g$graph.s$set symbol size 1.5
#g$graph.s$set symbol linewidth 3.0
#g$graph.s$set line type 0
#g$graph.s$set symbol color 1
#g$graph.s$set comment "$fn"
$set++;
}


sub LoadFileRee2
{
  $fn=shift;

  ($lam)= $fn =~ /s(\d\d\d)/;
  if ($fn =~ /x$/) {$l=$lam/100;}
  if ($fn =~ /y$/) {$l=1.0/sqrt($lam/100);}

  $simsym=1;
  if ($fn=~/s200/ and $fn=~/x$/ ) { $simsym=8; }
  if ($fn=~/s200/ and $fn=~/y$/ ) { $simsym=9; }
  if ($fn=~/s400/ and $fn=~/x$/ ) { $simsym=2; }
  if ($fn=~/s400/ and $fn=~/y$/ ) { $simsym=3; }

  $xold=0;
  open FI,"$fn";
  while ()
     {
        ($n,$x)=split ;
        $x/=$b2*$n;

        next unless &$filter($n,$xold);

#g$graph.s$set point $n,$x
        $xold=$n;
     }
  close(FI);

#g$graph.s$set symbol $simsym
#g$graph.s$set symbol size 1.5
#g$graph.s$set symbol linewidth 3.0
#g$graph.s$set line type 0
#g$graph.s$set symbol color 1
#g$graph.s$set comment "$fn"
$set++;
}

sub LoadFile
{
  $fn=shift;

  ($lam)= $fn =~ /s(\d\d\d)/;
  if ($fn =~ /qx$/) {$l=$lam/100;}
  if ($fn =~ /qy$/) {$l=1.0/sqrt($lam/100);}

  $simsym=1;
  if ($fn=~/s200/ and $fn=~/x_n$/ ) { $simsym=8; }
  if ($fn=~/s200/ and $fn=~/y_n$/ ) { $simsym=9; }
  if ($fn=~/s400/ and $fn=~/x_n$/ ) { $simsym=2; }
  if ($fn=~/s400/ and $fn=~/y_n$/ ) { $simsym=3; }
  
  if ($fn=~/SSS20/i)  {$col=2;}
  if ($fn=~/SSS100/i) {$col=3;}
  if ($fn=~/SSS500/i) {$col=4;}

  $pat=0;
  if ($fn=~ m|/ga|i) {$pat=1;}

  $count=0;
  open FI,"$fn" or die "$! $fn\n";
  while ()
    {
      next if (/^#/);
      ($x,$y)= split ;
   
      $xtab[$count]=$x;
      $ytab[$count]=$y;
      $count++;
    }
  close FI;

  $xold=0;
  for ($i=0;$i<$count;$i++)
    {
      $x=$xtab[$i];
      $y=$ytab[$i];
      
      if (&$filter($x,$xold))
       {
#g$graph.s$set point $x,$y
          $xold=$x;
        }
    }

#g$graph.s$set symbol $simsym
#g$graph.s$set symbol size 1.5
#g$graph.s$set symbol linewidth 3.0
#g$graph.s$set line type 0
#g$graph.s$set symbol color $col
#g$graph.s$set comment "$fn"
#g$graph.s$set symbol fill color $col
#g$graph.s$set symbol fill pattern $pat

$set++;
}

sub log10 { $x=shift; return log($x)/log(10);}

sub LoadTheory
{
  $fn=shift;

  $count=0;
  open FI,"$fn" or die "$! $fn\n";
  while ()
    {
      next if (/^#/);
      ($x,$y)= split ;

#g$graph.s$set point $x,$y
    }
  close FI;

#s$set symbol 0
#s$set line type 1
#s$set line linestyle $ls
#g$graph.s$set line linewidth $lw
#s$set line color 1
#s$set comment "theory $fn"

  $set++;
}

##defines an axis

sub Axis
{
$set=0;
$graph=shift;

#g$graph on
#with g$graph
#world ymin 0.25
#world ymax 18
#world xmin 1.01
#world xmax 999

#xaxis  ticklabel skip 0

#yaxes scale logarithmic
#yaxis tick minor ticks 0
#yaxis tick major 4
#yaxis tick major size 0.5
#yaxis ticklabel format general
#yaxis ticklabel prec 2
#yaxis ticklabel off

#xaxes scale logarithmic
#xaxis tick minor ticks 0
#xaxis tick major 10
#xaxis tick major size 0.5
#xaxis ticklabel format power
#xaxis ticklabel prec 0
#xaxis label "n"

#yaxis label char size 3.0
#xaxis label char size 3.0
#yaxis ticklabel char size 2.5
#xaxis ticklabel char size 2.5
#xaxis tick major size 1.5
#yaxis tick major size 1.5

#subtitle size 2.5
}


sub AxisRg
{
$graph=shift;
$set=0;

#g$graph on
#with g$graph
#world ymin 0.22
#world ymax 18
#world xmin 1.01
#world xmax 999
#xaxis ticklabel skip 0

#yaxes scale logarithmic
#yaxis tick minor ticks 0
#yaxis tick major 4
#yaxis tick major size 0.5
##yaxis ticklabel format power
#yaxis ticklabel prec 2
#yaxis ticklabel off

#xaxes scale logarithmic
#xaxis tick minor ticks 0
#xaxis tick major 10
#xaxis tick major size 0.5
#xaxis ticklabel format power
#xaxis ticklabel prec 0
#xaxis ticklabel off
#xaxis label ""

#yaxis label char size 3.0
#xaxis label char size 3.0
#yaxis ticklabel char size 2.5
#xaxis ticklabel char size 2.5
#xaxis tick major size 1.5
#yaxis tick major size 1.5

#subtitle size 2.5
}

## the filter handles equidistant spacing of datapoints on a logscale.

$stepping=1.65;
$filter=sub { my $x=shift; my $xold=shift; return ($x>$xold*$stepping); };


##select what to plot, notice the advantage of using systematic filenames.

sub Plot
{
$base=shift;

$stepping=1.65;
LoadFile("$dir/${base}s200.${path}500.qx_n"); 
LoadFile("$dir/${base}s200.${path}500.qy_n"); 
LoadFile("$dir/${base}s400.${path}500.qx_n"); 
LoadFile("$dir/${base}s400.${path}500.qy_n"); 

$stepping=1.45;
LoadFile("$dir/${base}s200.${path}100.qx_n"); 
LoadFile("$dir/${base}s200.${path}100.qy_n"); 
LoadFile("$dir/${base}s400.${path}100.qx_n"); 
LoadFile("$dir/${base}s400.${path}100.qy_n"); 

$stepping=1.25;
LoadFile("$dir/${base}s200.${path}20.qx_n"); 
LoadFile("$dir/${base}s200.${path}20.qy_n"); 
LoadFile("$dir/${base}s400.${path}20.qx_n"); 
LoadFile("$dir/${base}s400.${path}20.qy_n"); 


LoadTheory("$dir/ths200.${path}500.qx_n");
LoadTheory("$dir/ths200.${path}500.qy_n");
LoadTheory("$dir/ths400.${path}500.qx_n");
LoadTheory("$dir/ths400.${path}500.qy_n");
}

sub PlotRg
{
LoadFileRg("rg_abc/rg_data_scat/rg_scaled_s200.x");  AddTheoryRg();
LoadFileRg("rg_abc/rg_data_scat/rg_scaled_s200.y");  AddTheoryRg();
LoadFileRg("rg_abc/rg_data_scat/rg_scaled_s400.x");  AddTheoryRg();
LoadFileRg("rg_abc/rg_data_scat/rg_scaled_s400.y");  AddTheoryRg();
}

sub PlotRg2
{
LoadFileRg2("rg_abc/rg_data_scat/rg_s100.x");  AddTheoryRg();
LoadFileRg2("rg_abc/rg_data_scat/rg_s200.x");  AddTheoryRg();
LoadFileRg2("rg_abc/rg_data_scat/rg_s200.y");  AddTheoryRg();
LoadFileRg2("rg_abc/rg_data_scat/rg_s400.x");  AddTheoryRg();
LoadFileRg2("rg_abc/rg_data_scat/rg_s400.y");  AddTheoryRg();
}

sub PlotRee
{
LoadFileRee("mdi/xc80l3500t1s200.SSS500.sx");  AddTheoryRee();
LoadFileRee("mdi/xc80l3500t1s200.SSS500.sy");  AddTheoryRee();
LoadFileRee("mdi/xc80l3500t1s400.SSS500.sx");  AddTheoryRee();
LoadFileRee("mdi/xc80l3500t1s400.SSS500.sy");  AddTheoryRee();
}

sub PlotRee2
{
LoadFileRee2("mdi/xc80l3500t1s100.SSS500.x");  AddTheoryRee();
LoadFileRee2("mdi/xc80l3500t1s200.SSS500.x");  AddTheoryRee();
LoadFileRee2("mdi/xc80l3500t1s200.SSS500.y");  AddTheoryRee();
LoadFileRee2("mdi/xc80l3500t1s400.SSS500.x");  AddTheoryRee();
LoadFileRee2("mdi/xc80l3500t1s400.SSS500.y");  AddTheoryRee();
}

$dC=$dC_rnd;
$pat=0;

##generate axis labels, look at the xmgrace manual for  \x and so on.

AxisRg(0);
#xaxis ticklabel on
#yaxis ticklabel on
#yaxis  label place spec
#yaxis  label place 0.0, 0.15
#yaxis label char size 3.5
#yaxis label "\xl\f{}\seff\N\S2\N"
#subtitle "R\sg\N\S2\N(N\sl\N;\xl\sa\N\f{})/R\sg\N\S2\N(N\sl\N)"

#xaxis ticklabel on
#xaxis label "N\sl\N"
$ls=1; $lw=2; $Phi=$Phi_rnd;  PlotRg();


AxisRg(1);

#xaxis ticklabel on
#subtitle "R\sg\N\S2\N(N\sl\N;\xl\sa\N\f{})/[b\S2\N(N\sl\N-1)/6]"
#xaxis ticklabel on
#xaxis label "N\sl\N"
$ls=1; $lw=2; $Phi=$Phi_rnd;  PlotRg2();



AxisRg(2);
#xaxis ticklabel on
#subtitle "r\s\xa\f{}\N\S2\N(n;\xl\sa\N\f{})/r\s\xa\f{}\N\S2\N(n)"
#subtitle size 2.75

#xaxis ticklabel on
#xaxis label "n"
$ls=1; $lw=2; $Phi=$Phi_rnd;  PlotRee();


AxisRg(3);

#xaxis ticklabel on
#subtitle "r\s\xa\f{}\N\S2\N(n;\xl\sa\N\f{})/[b\S2\Nn]"
#xaxis ticklabel on
#xaxis label "n"
#subtitle size 2.75

$ls=1; $lw=2; $Phi=$Phi_rnd;  PlotRee2();







$dir="inversion";
$path='SSS';

Axis(4);
#subtitle "q\s\xa\f{}\N\S2\N(n)/q\s\xa\f{}\N\S2\N(n,\xl\sa\f{}\N)"

$ls=1; $lw=2;
Plot("ex");


$dir="inversion";
Axis(5);
#subtitle "q\s\xa\f{}\N\S2\N(n)/q\s\xa\f{}\N\S2\N(n,\xl\sa\f{}\N)"
Plot("ga");

## I need to control how graphs are positioned, and where labels are inserted in graphs.

$width=0.68;
$xmin=0.22;
for ($n=0; $n<=$graph; $n++)
{
$s=chr($n+97);

if ($n==2) {$xmin+=0.1;}
if ($n==4) {$xmin+=0.1;}
$xlab=$xmin+0.03;
$xmax=$xmin+$width;

#with g$n
#    view xmin $xmin
#    view xmax $xmax
#    view ymin 0.210000
#    view ymax 0.850000

#with string
#    string on
#    string loctype view
#    string $xlab, 0.75
#    string char size 3.000000
#    string def "$s)"
    
$xmin+=$width;
}

#redraw

Running the script above (if you had all my datafiles!) produce: