Plate - plate tectonic modelling functionality in Perl.
# be smart use strict; # import required symbols use Plate qw(:tag func $scalar); # your code goes here
Plate is a Perl module that provides basic functionality for plate tectonic modelling.
The essence of this module lies in the way data are stored, and
consequentially, how they can be accessed. Data are stored in a data hash. The keys
of a data hash are the data id's. The values
are references to item hashes, one for each item in a data file. The keys
of an item
hash are partly predefined, as will be explained below, but can be supplemented
in a user program. The values
of an item hash are stored by value if they are scalar, and by reference if they are not.
Since data are stored in hashes, an item id doesn't have to be numerical,
but id's like ``12'' and ``12a'' are allowed.
The content of an item hash can be accessed in two general ways, directly and indirectly. Directly means that the actual item data are accessed, and modifications are effective for the remainder of the program. Indirect access means that a copy of the data is made, and modifications are only effective inside the scope where the copy is valid. Illustrating with vertex data:
# read the data my %verts = &read_verts("./nodes.dat");
my($id, %vert); foreach $id (keys %verts) { %vert = %{$verts{$id}}; # indirect access, make a copy print "vertex $id contains ", $vert{LON()}, "\n"; $vert{LON()} = 0; # change a value in the copy } # actual data remain unchanged
my $vert; foreach $id (keys %verts) { $vert = $verts{$id}; # direct access through the reference print "vertex $id contains ", $vert->{LON()}, "\n"; $vert->{LON()} = 0; # change a value in the actual data }
Since a hash cannot contain non-scalar values
, all complex data are stored by reference. Therefore, the values
of complex data must be dereferenced in order to access their content. As a
continuation of the previous example:
foreach $id (keys %verts) { %vert = %{$verts{$id}}; # indirect access, make a copy print "vertex $id contains ", @{$vert{LOC()}}, "\n"; }
foreach $id (keys %verts) { $vert = $verts{$id}; # direct access through the reference print "vertex $id contains ", @{$vert->{LOC()}}, "\n"; }
As an aside, direct data acces can also be obtained by fully indexing (and possibly dereferencing) the data hash, although this is more difficult to read:
foreach $id (keys %verts) { # direct access through indexing $verts{$id}{LON()} = 0; print @{$vert{$id}{LOC()}}, "\n"; }
The flexibility of Perl becomes obvious when data need to be added to the data hash. The only thing that must be kept in mind, is that complex data are stored by reference (did I mention this before?). For example:
foreach $id (keys %verts) { $vert = $verts{$id}; # add new data $vert->{"my_scalar"} = 10; $vert->{"my_vector"} = [1, 2, 3]; # anonymous @{$vert->{"another_vector"}} = &vector(3, 2, 1); }
foreach $id (keys %verts) { $vert = $verts{$id}; print "vertex $id contains ", @{$vert->{"my_vector"}}, "\n"; }
Several types of modelling data can be set up with this module. This is
typically done by reading one or more data files, and then doing the
required processing. A number of keys
are predefined and their associated values
initialized when a data file is read into a hash:
Keys: LON (longitude), LAT (latitude), LOC (location vector), TRAILER (additional information from the data file).
Keys: LON (longitude centroid), LAT (latitude centroid), LOC (location vector centroid), N1, N2, N3 (vertices 1, 2, 3), AREA (element area), TRAILER (additional information from the data file).
Keys: LON (longitude centroid), LAT (latitude centroid), LOC (location vector centroid), N1, N2 (vertices 1, 2), LENGTH (element area), NORMAL (carthesian normal vector), TRAILER (additional information from the data file)
Vertex hashes are special, since other data hashes
are defined on the data contained in a vertex data hash
. The values of data hashes
associated with the N1, N2, and N3 keys are references to the item hashes
of a vertex
data hash
, and therefore can be treated identically.
In principle, all calculations (lengths, areas) are done on the sphere.
Plate defines the following constants. Since use strict;
limits the way in which barewords can be used, it is probably best to get
used to appending ()
to the constant identifiers, as is done here.
The identifiers for hash value access will help avoid clashes with elements that are added later. Internally, these start with an underscore, but the actual content might be subject to change or extension.
longitude
latitude
carthesian location vector
vertices
area
length
carthesian normal vector (unit length)
trailing information in data files
Plate defines the following variables.
The flag that indicates whether existing files may be overwritten. If 'true', write routines will warn and exit when a write to an existing file is attempted. Write protection is on by default. This variable is not exported, and can only be called through a full package reference.
The flag that indicates whether verbose messages should be printed. It can be directly modified, but the module also provides the &verbose() subroutine. Verbose mode is on by default. This variable is not exported, and can only be called through a full package reference.
Read a set of vertex locations into a data hash. File format is
id lon lat trailer
one vertex per line. Trailing information is stored as a string in the
TRAILER field. Returns a vertex data hash
, or an empty list.
Write a vertex data hash into a file. File format is
id lon lat trailer
one vertex per line. Data spacing is controlled by $,
and trailing information is only appended if present in the TRAILER field.
No sorting is done. Returns the number of vertices written. If the file
name is '-', output is written to the currently selected device.
Read a set of triangular elements into a data hash. File format is
id n1 n2 n3 trailer
one element per line. The fields n1, n2, and n3 are the vertex id's as they
exist in the vertex data hash
that is passed by reference. Trailing information is stored as a string in
the TRAILER field. Returns an element
data hash
, or an empty list.
The functionality built into Plate assumes positive element definitinions, ie. going from vertex 1 to vertex 2 to vertex 3 back to vertex 1 means that the boundary of the element is travelled according to the right hand rule (corkscrew rule) with respect to the centroid location vector. Don't bother editing element definitions, this routine checks the 'handedness' and corrects if necessary, by exchanging n2 and n3. Consequently, reading and writing the element definitions will correct a data file.
Write an element data hash into a file. File format is
id n1 n2 n3 trailer
one element per line. Data spacing is controlled by $,
and trailing information is only appended if present in the TRAILER field.
No sorting is done. Returns the number of elements written. If the file
name is '-', output is written to the currently selected device.
Read a set of edges into a data hash. File format is
id n1 n2 trailer
one edge per line. The fields n1, and n2 are the vertex id's as they exist
in the vertex data hash
that is passed by reference. Trailing information is stored as a string in
the TRAILER field. Returns an edge data hash
, or an empty list.
The functionality built into Plate uses a normal to the edge which is defined as the outer product of the location vector of vertex 1 with that of vertex 2. Thus The normal lies in the surface of the sphere, and points to the left when looking from vertex 1 to vertex 2.
Write an edge data hash into a file. File format is
id n1 n2 trailer
one edge per line. Data spacing is controlled by $,
and trailing information is only appended if present in the TRAILER field.
No sorting is done. Returns the number of edges written. If the file name
is '-', output is written to the currently selected device.
Reads scalar data into the item hashes of a data hash. File format is
id scalar trailer
The scalars are stored by value in fields of the item hashes keyed by
$key
. If a (non-zero) $trailer
is specified, any additional information trailing the scalar data is also
stored into the item hashes, keyed by the value of $trailer
. Returns the number of scalars read.
Reads vector data into the item hashes of a data hash. File format is
id x y z trailer
The vectors are stored by value in fields of the item hashes keyed by
$key
. If a (non-zero) $trailer
is specified, any additional information trailing the vector data is also
stored into the item hashes, keyed by the value of $trailer
. Returns the number of vectors read.
Write a value from the items in a data hash into a file. File format is
id (data) trailer
one item per line. Data spacing is controlled by $,
. Data can either be scalar or vector data, depending on the type of value
associated with the
$key
parameter, which specifies which value from the item hashes should be written. Custom data types are not supported, but it is easy to
roll your own little IO routine. It is assumed that the specified key might
not exist for all data items, and therefore lookups of the value will fail
silently. Trailing information is appended if the trailer
parameter contains a key that exists in the item hash. The routine will try to do the right thing when the trailer is found to
be a reference to an array or a hash, by printing a string of elements or (key, value)
pairs. Otherwize, the value is interpreted as a scalar. No sorting is done.
Returns the number of lines written. If the file name is '-', output is
written to the currently selected device.
Looks up the id of an item in a data hash, if only the reference to the item hash is known. Returns the id, or undef. Note, this might be slow for large data hashes, so use sparingly.
Sets write protection mode for this module to the logical value of the first parameter passed. If no argument is given, write protection mode is toggled. Returns true if write protection mode is set, false otherwize. Write protection mode is on by default. This subroutine is not exported, and can only be called through a full package reference.
Sets verbose mode for this module to the logical value of the first parameter passed. If no argument is given, verbose mode is toggled. Returns true if verbose mode is set, false otherwize. Verbose mode is on by default. This subroutine is not exported, and can only be called through a full package reference.
#!/usr/bin/perl # Plate test bed
use strict; # import required symbols use Carth3 qw(:FUNCTIONS); use Coords qw(:VARIABLES :FUNCTIONS); use Plate qw(:CONSTANTS :VARIABLES :FUNCTIONS);
$, = "\t"; # get decent alignment
$NEGATIVE_LON = 1;
my %nodes = &read_verts("./nodes.dat"); my $n_nodes = keys %nodes; my %elems = &read_elems("./elems.dat", \%nodes); my $n_elems = keys %elems; my %lines = &read_edges("./lines.dat", \%nodes); my $n_lines = keys %lines;
print "read ", $n_nodes, " nodes, ", $n_elems, " elements, ", $n_lines, " lines\n\n";
my($id, $line);
foreach $id (keys %lines) { $line = $lines{$id}; # direct access through the reference $line->{"test"} = {'a_key' => 'a_value'}; }
print "\n"; &write_edges("-", \%lines, \%nodes); print "\n"; &write_item_data("-", \%lines, LOC(), "test");
perl(1),
Carth3(3),
Coords(3),
Rotate(3).
Evangelization bit: to avoid namespace clashes, typing errors and lots of other problems, I
strongly recommend using strict
.
No symbols are exported into the caller's namespace, but in stead, all required symbols should be explicitly imported, or referred to by using the package name. For ease of use, the export tags CONSTANTS, VARIABLES, and FUNCTIONS are defined.
Perl interprets the number '0' in the same way as the empty string, as 'FALSE'. This may not exactly be what you want. Therefore, if you want to define a numerical zero value in a data file, write it as '0.0', which is interpreted correctly.
Most subroutines perform some level of error checking (vagueness intentional). Error messages are printed on STDERR if verbose mode is set, and should be self-explanatory. Furthermore, the value a subroutine returns is a good indication whether something went wrong, so check it.
Currently only triangular elements are implemented, simply because I have no need for more.
Although using text keys
for the data hashes provides more flexibility, it comes at a small price. Sorting of these
hashes by key is done by comparing them as text, not numerically (eg. 10
comes before 2). Of course the sort can be forced to compare keys
numerically, but the results may be unexpected for non-numerical keys
. One solution might be to pad id's with leading zero's (eg. 02 comes
before 10).
Joor Loohuis, loohuis@geo.uu.nl
You can determine which version of Plate you are using, by printing the
$Plate::VERSION
variable from within your script.
Version numbering follows a scheme which is rapidly becoming accepted, in which the version minor (the first number after the first period) indicates whether a stable (even version minor) or a development version (odd version minor) is meant. Version majors (first number before first period) of 0 (zero) are all more in development than others.
Copyright (c) 1999 Joor Loohuis. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.