File: InitialConfigTut.py

package info (click to toggle)
esys-particle 2.1-4
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 7,284 kB
  • sloc: cpp: 77,304; python: 5,647; makefile: 1,176; sh: 10
file content (380 lines) | stat: -rw-r--r-- 16,151 bytes parent folder | download
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
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
#############################################################
##                                                         ##
## Copyright (c) 2003-2011 by The University of Queensland ##
## Earth Systems Science Computational Centre (ESSCC)      ##
## http://www.uq.edu.au/esscc                              ##
##                                                         ##
## Primary Business: Brisbane, Queensland, Australia       ##
## Licensed under the Open Software License version 3.0    ##
## http://www.opensource.org/licenses/osl-3.0.php          ##
##                                                         ##
#############################################################
__docformat__ = "restructuredtext en"

import esys.lsm.doc.Util
from esys.lsm.util     import Vec3, BoundingBox, seedDefaultRng
from esys.lsm.geometry import SimpleSphere,SimpleBlock, CubicBlock
from esys.lsm.geometry import HexagBlock, RandomBoxPacker, ParticleCollection
from esys.lsm.geometry import RandomSpherePacker

__initCfgTutSection = \
"""
Tutorial 2: Creating Initial Configurations of Particles
========================================================

Part of designating the initial conditions for a discrete element
simulation is to specify a starting configuration of particles.
The `esys.lsm.geometry` package provides classes for generating
basic initial *packings* of spherical particles. This tutorial
demonstrates the use of these classes for generating various
types of packings.

Regular Sphere Packings of Uniformly Sized Spheres
--------------------------------------------------

There are three classes for generating rectangular blocks of
regularly-packed particles:
  
  `esys.lsm.geometry.SimpleBlock`
    Spheres packed such that their centre points form
    a regular grid. Each sphere has a maximum of six
    contacting neighbours.
  `esys.lsm.geometry.CubicBlock`
    Spheres packed in a cubic close packing.
  `esys.lsm.geometry.HexagBlock`
    Spheres packed in a hexagonal close packing.

A good introduction to sphere packing, which defines
*cubic close* and *hexagonal close* packings, can be
found at http://mathworld.wolfram.com/SpherePacking.html.

The following Python code illustrates the generation of three
packing configurations::

  from esys.lsm.geometry import SimpleBlock,CubicBlock,HexagBlock

  simpleBlk = SimpleBlock(dimCount = [10,20,5], radius = 0.5)
  cubicBlk  = CubicBlock(dimCount = [20,20,20], radius = 0.25)
  hexagBlk  = HexagBlock(dimCount = [5,10,15], radius = 1.0)

The ``SimpleBlock``, ``CubicBlock`` and ``HexagBlock`` constructors
each take two arguments:
  
  ``dimCount``
    A three-element list of integers indicating the number of particles
    in each axis direction.
  ``radius``
    The radius of each particle in the packing.

Thus, the ``simpleBlk`` packing, consists of ``10*20*5=1000`` particles
of radius ``0.5`` arranged in an axis-aligned block which is ``10`` particles
wide in the *x* direction, ``20`` particles high in the *y* direction and
``5`` particles deep in the *z* direction. Similarly, the ``cubicBlk``
packing consists of ``20*20*20=8000`` particles of radius ``0.25`` and
the ``hexagBlk`` packing consists of ``5*10*15=750`` particles of radius
``1.0``. The ``cubicBlk`` and ``hexagBlk`` packings are produced by arranging
2D layers of spheres on top of one another. The 2D layers themselves are
produced by arranging linear rows of spheres in a regular triangular packing.
By default, each row is aligned with the *x* axis and the 2D layers are
generated parallel to the *x*-*z* plane. This default arrangement is referred
to as an ``XZY`` *orientation* (``XZ`` layers packed in the ``Y`` direction).
The ``HexagBlock`` and ``CubicBlock`` constructors accept an additional
``orientation`` keyword argument, for specifying alternative orientations.
For example, the above assignment statements involving the ``cubicBlk``
and ``hexagBlk`` variables are equivalent to::

  from esys.lsm.geometry import SimpleBlock,CubicBlock,HexagBlock,Orientation

  cubicBlk  = CubicBlock(dimCount = [20,20,20], radius = 0.25, orientation = Orientation.XZY)
  hexagBlk  = HexagBlock(dimCount = [5,10,15], radius = 1.0, orientation = Orientation.XZY)

Alternative ``orientation`` values produce different layer packings. For example::

  hexagBlk  = HexagBlock(dimCount = [5,10,15], radius = 1.0, orientation = Orientation.YZX)

specifies an ``YZX`` orientation. This produces a packing where ``10``
linear-rows (each row consisting of ``5`` particles) parallel with the
*y*-axis, are stacked into a layer which is parallel with the *y*-*z*
plane. ``15`` of these *y*-*z* layers are then packed in the *x* direction
to form the block.

Packings of Randomly Sized Spheres
----------------------------------

The following script generates a packing of randomly sized spheres
within an axis-aligned box::

  from esys.lsm.geometry import RandomBoxPacker
  from esys.lsm.util     import Vec3, BoundingBox, seedDefaultRng

  minR = 0.2
  maxR = 1.0
  seedDefaultRng(123454321)
  rndPkr = \\
    RandomBoxPacker(
        minRadius = minR,
        maxRadius = maxR,
        cubicPackRadius = maxR,
        maxInsertFails = 32000,
        bBox = BoundingBox(Vec3(0,0,0), Vec3(10,20,5)),
        circDimList = [False,False,False],
        tolerance = 0.01*minR
    )
  rndPkr.generate()
  rndBlk = rndPkr.getSimpleSphereCollection()
  print "Number of Spheres = ", len(rndBlk)

These statements generate a packing of spheres with random radius
values ranging between 0.2 and 1.0. After initialising the
minimum radius (``minR``) and maximum radius (``maxR``) variables,
a `RandomBoxPacker` object is created and assigned to the ``rndPkr``
variable. The ``RandomBoxPacker`` constructor accepts the following
arguments:
  
  ``minRadius``
    Minimum radius of generated spheres.
  ``maxRadius``
    Maximum radius of generated spheres.
  ``cubicPackRadius``
    Initial randomly sized seed particles
    are generated at positions corresponding to a regular
    cubic-packed grid of spheres with radius ``cubicPackRadius``.
  ``maxInsertFails``
    Stopping criterion for terminating the random
    insertion algorithm. Algorithm terminates after ``maxInsertFails``
    number of attempts at fitting a randomly generated particle into
    the box.
  ``bBox``
    A box specifying the region into which particles are packed.
    A 2D packing can be generated by specifying a zero sized *z* dimension,
    eg ``bBox=BoundingBox(Vec3(1,1,0),Vec3(21,21,0))`` will generate a 2D
    packing in the *x*-*y* plane.
  ``circDimList``
    A list of 3 boolean values indicating which (if any)
    of the box dimensions is circular (note, only a single dimension may
    be circular).
  ``tolerance``
    Generated particles may overlap by no more than this amount.

The construction of the ``RandomBoxPacker`` object does not produce
the packing, the packing is generated by the call to the
`RandomBoxPacker.generate` method (ie the ``rndPkr.generate()``
statement). The collection of random-sized particles
is assigned to the ``rndBlk`` variable. In this example, with
the above parameters, the ``rndBlk`` collection contains
``len(rndBlk) == 3501`` `SimpleSphere` objects.

The packing algorithm (executed in the ``RandomBoxPacker.generate`` method)
has two main parts: the generation of an initial group of *seed* spheres
and random insertion of *fill-in* spheres. The seed sphere radii are
selected from a uniformly random distribution, and the seed sphere centre
positions cooincide with the centre points of spheres of radius
``cubicPackRadius`` arranged in a regular cubic close packing.
Fill-in spheres are then generated by randomly choosing locations
within the specifed ``bBox`` box and attempting to find a sphere which
touches neighbouring spheres (or touches neighbouring spheres and a side
of the box) and which has radius in lying in the interval
``[minRadius,maxRadius]``. If no sphere can be found which fits with
neighbouring spheres and the boundary then this is recorded as a failed
insertion attempt and another random location is selected. When there
have been ``maxInsertFails`` *consecutive* failed insertion attempts,
the algorithm stops and the ``RandomBoxPacker.generate`` method returns.

Useful Methods for Manipulating `SimpleSphere` Collections
----------------------------------------------------------

The `SimpleBlock`, `CubicBlock` and `HexagBlock` are all subclasses
of the `ParticleCollection` class while the `RandomBoxPacker` and
`RandomSpherePacker` classes provide a ``getParticleCollection``
method for obtaining a ``ParticleCollection`` object.
An object of class ``ParticleCollection`` is simply a
list/group/collection of `SimpleSphere` objects. The ``ParticleCollection``
class provides methods which are useful for manipulating
the group of `SimpleSphere` objects. This section provides
some examples of using these methods.

Geometrical Transformations: Translation and Rotation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Once a basic arrangement of spheres has been generated it is
often useful to be able to translate, or rotate all of the
spheres in the collection. Objects of class `ParticleCollection`
provide the `ParticleCollection.translate` and
`ParticleCollection.rotate` methods which translate and rotate
all ``SimpleSphere`` objects contained in the collection::

  from esys.lsm.geometry import SimpleBlock
  from esys.lsm.util     import Vec3
  from math              import pi
  
  smplBlk = SimpleBlock(dimCount=[10,3,1], radius=0.5)
  smplBlk.translate(translation = Vec3(-5, 0, 0))
  smplBlk.rotate(axis = Vec3(0, 0, pi/4.0), pt = Vec3(0, 0, 0))

Here the ``smplBlk`` variable is assigned to a collection of 30
``SimpleSphere`` objects arranged in a regular grid. The ``translate``
method translates all particles in the collection by ``5`` units in
the negative *x* direction. All the ``SimpleSphere`` objects are then
rotated by ``pi/4.0`` radians counterclockwise about the *z* axis.

Iterating
^^^^^^^^^

The ``ParticleCollection`` class supports the python *iterable*
concept, meaning that individual elements of the
collection can be enumerated in a loop as follows::
  
  from esys.lsm.geometry import SimpleBlock
  from esys.lsm.util     import Vec3
  from math              import pi
  
  smplBlk = SimpleBlock(dimCount=[10,3,1], radius=0.5)
  for ss in smplBlk:
      ss.translate(Vec3(-5,0,0))
      ss.rotate(axis = Vec3(0, 0, pi/4.0), pt = Vec3(0, 0, 0))

During each iteration in the ``for`` loop, the *loop variable*
``ss`` is assigned to a ``SimpleSphere`` object in the ``smplBlk``
collection. The above statements result in a final state which is
equivalent to the transformations performed by directly calling
methods of the ``smplBlk`` object. That is, the above ``for``
loop is equivalent to::
  
  smplBlk = SimpleBlock(dimCount=[10,3,1], radius=0.5)
  smplBlk.translate(translation = Vec3(-5, 0, 0))
  smplBlk.rotate(axis = Vec3(0, 0, pi/4.0), pt = Vec3(0, 0, 0))

Saving to File
^^^^^^^^^^^^^^

Often it is the case that it is desired to run multiple simulations
with the same initial configuration of spheres. Rather than generating
the packing for each simulation, it is convenient to save the initial
configuration of spheres to file and read in this configuration
before executing the simulation. Saving the configuration to file is
also convenient when it is time consuming to generate the initial
configuration, that is, often it is much faster to read a packing
of spheres from file rather than compute the positions of particles
in the packing each time the data is needed.

There are two options for *serializing* the data: custom file formats
and Python's built-in serialization modules
pickle_ and cPickle_.

.. _pickle: http://www.python.org/doc/2.4.2/lib/module-pickle.html
.. _cPickle: http://www.python.org/doc/2.4.2/lib/module-cPickle.html

Custom File Formats
~~~~~~~~~~~~~~~~~~~

Python's I/O interfaces and simple conversion from basic types
to strings (and vice versa) make it relatively simple to save
any kind of data to file::

  from esys.lsm.geometry import HexagBlock
  
  hexagBlk = HexagBlock(dimCount=[10,10,10], radius=0.25)
  f = file("HexagBlk.txt", "w")
  for ss in hexagBlk:
      f.write(
          "%%s %%s %%s %%s\\n"
          %%
          (ss.getId(), ss.getRadius(), ss.getCentre(), ss.getMass())
      )
  f.close()

After constructing the hexagonal close packing of 1000 spheres
(and assigning it to the ``hexagBlk`` variable),
the file_ ``"HexagBlk.txt"`` is opened in *write* mode.
The ``for`` loop then iterates over each ``SimpleSphere`` object
in the ``hexagBlk`` collection and writes a line to the
``"HexagBlk.txt"`` file. The ``(%%s %%s %%s %%s\\n) %% (...)``
argument to the ``f.write`` method call is a `string formatting
operation`__ and is semantically similar to ``sprintf`` in C.

__ http://www.python.org/doc/2.4.2/lib/typesseq-strings.html
.. _file: http://www.python.org/doc/2.4.2/lib/built-in-funcs.html#l2h-25

The data can be read from file to reconstruct a ``HexagBlock``
as follows::

  from esys.lsm.geometry import HexagBlock, SimpleSphere
  from esys.lsm.util     import Vec3
  
  hexagBlk = HexagBlock()
  for line in file("HexagBlk.txt", "r"):
      elemList = line.split(" ")
      hexagBlk.createParticle(
        SimpleSphere(
            id     = int(elemList[0]),
            radius = float(elemList[1]),
            centre = Vec3(map(float,elemList[2:5])),
            mass   = float(elemList[5])
        )
      )

First the ``hexagBlk`` variable is assigned to an empty
``ParticleCollection`` object. The ``for`` loop iterates over
each line in the ``"HexagBlk,txt"`` file. The ``line`` string
is split into a list of string elements, which are delimited
by the ``" "`` space character, and this list is assigned to
the ``elemList`` variable. In the next statement, a ``SimpleSphere``
object is created within the ``hexagBlk`` collection by calling
the ``hexagBlk.createParticle`` method. The arguments to the
``SimpleSphere`` constructor are converted from strings to either
an ``int`` value or ``float`` values. The call to
``map(float, elemList[2:5])`` converts the ``elemList[2:5]``
list-slice of three string elements to a list of three ``float``
elements.

Saving data in a custom file format offers the advantage of
easily being able to observe the data in a text file. Explicity
saving data in a specific format may also make it possible to use
other software to visualise/analyse the data.

Pickling
~~~~~~~~

Python's pickle_ and cPickle_ modules provide a convenient mechanism
for serializing Python objects. In Python-speak, serialization
is called *pickling* and de-serialization is called *unpickling*.
The following Python code pickles the same data as that of the
`Custom File Formats`_ section::

  from esys.lsm.geometry import HexagBlock
  import pickle
  
  hexagBlk = HexagBlock(dimCount=[10,10,10], radius=0.25)
  f = file("HexagBlk.pkl", "w")
  pickle.dump(obj = hexagBlk, file = f, protocol = pickle.HIGHEST_PROTOCOL)
  f.close()

As usual, the ``hexagBlk`` is assigned to a hexagonal close-packed
collection of ``SimpleSphere`` objects. The file ``HexagBlk.pkl``
is opened in *write* mode, and the call to the ``pickle.dump`` function
takes care of pickling the ``hexagBlk`` object to file.
The data is unpickled by executing::
  
  import pickle
  
  f = file("HexagBlk.pkl", "r")
  hexagBlk = pickle.load(file = f)
  f.close()

The ``HexagBlk.pkl`` file is opened in *read* mode and
the call to ``pickle.load`` unpickles the data from
the file into the original pickled object and this
is assigned to the ``hexagBlk`` variable.

The advantage of the pickling technique is that it requires far
fewer lines of code and the user doesn't have to know the internal
structure of the data being saved. The main disadvantage is that
the saved data-file is not human-readable and is only loadable
by Python.

"""


__doc__ = \
    esys.lsm.doc.Util.setSectionDoc("InitialConfigSection",__initCfgTutSection) \
    + "\n:summary: Creating packings of spheres and saving/loading packing-data from file.\n"