File: pycad.tex

package info (click to toggle)
python-escript 5.6-10
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 144,304 kB
  • sloc: python: 592,074; cpp: 136,909; ansic: 18,675; javascript: 9,411; xml: 3,384; sh: 738; makefile: 207
file content (882 lines) | stat: -rw-r--r-- 33,469 bytes parent folder | download | duplicates (3)
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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright (c) 2003-2018 by The University of Queensland
% http://www.uq.edu.au
%
% Primary Business: Queensland, Australia
% Licensed under the Apache License, version 2.0
% http://www.apache.org/licenses/LICENSE-2.0
%
% Development until 2012 by Earth Systems Science Computational Center (ESSCC)
% Development 2012-2013 by School of Earth Sciences
% Development from 2014 by Centre for Geoscience Computing (GeoComp)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\chapter{The \pycad Module}\label{PYCAD CHAP}

\section{Introduction}

\pycad provides a simple way to build a mesh for your finite element
simulation. You begin by building what we call a \class{Design} using
primitive geometric objects, and then go on to build a mesh from this.
The final step of generating the mesh from a \class{Design} uses freely
available mesh generation software, such as \gmshextern.

A \class{Design} is built by defining points, which are used to specify
the corners of geometric objects and the vertices of curves. Using
points you construct more interesting objects such as lines,
rectangles, and arcs. By adding many of these objects into a \class{Design},
you can build meshes for arbitrarily complex 2-D and 3-D structures.

\section{The Unit Square}
The simplest geometry is the unit square. First we generate the corner points
\begin{python}
  from esys.pycad import *
  p0=Point(0.,0.,0.)
  p1=Point(1.,0.,0.)
  p2=Point(1.,1.,0.)
  p3=Point(0.,1.,0.)
\end{python}
which are then linked to define the edges of the square
\begin{python}
  l01=Line(p0,p1)
  l12=Line(p1,p2)
  l23=Line(p2,p3)
  l30=Line(p3,p0)
\end{python}
The lines are put together to form a loop
\begin{python}
  c=CurveLoop(l01,l12,l23,l30)
\end{python}
The orientation of the line defining the \class{CurveLoop} is important.
It is assumed that the surrounded area is to the left when moving along the
lines from their starting points towards the end points.
Moreover, the line needs to form a closed loop.
We now use the \class{CurveLoop} to define a surface
\begin{python}
  s=PlaneSurface(c)
\end{python}
Note that there is a difference between the \class{CurveLoop}, which defines
the boundary of the surface, and the actual surface \class{PlaneSurface}.
This difference becomes clearer in the next example with a hole.
Now we are ready to define the geometry which is described by an instance of
the \class{Design} class:
\begin{python}
  d=Design(dim=2,element_size=0.05)
\end{python}
Here we use the two-dimensional domain with a local element size in the finite
element mesh of $0.05$.
We then add the surface \code{s} to the geometry
\begin{python}
  d.addItems(s)
\end{python}
This will automatically import all items used to construct \code{s} into the
\class{Design} \code{d}.
Now we are ready to construct a \finley FEM mesh and then write it to the file
\file{quad.fly}:
\begin{python}
  from esys.finley import MakeDomain
  dom=MakeDomain(d)
  dom.write("quad.fly")
\end{python}
In some cases it is useful to access the script used to generate the geometry.
You can specify a specific name for the script file. In our case we use
\begin{python}
  d.setScriptFileName("quad.geo")
\end{python}
It is also useful to check error messages generated during the mesh generation
process. \gmshextern writes messages to the file \file{.gmsh-errors} in your
home directory.
Putting everything together we get the script
\begin{python}
  from esys.pycad import *
  from esys.pycad.gmsh import Design
  from esys.finley import MakeDomain
  p0=Point(0.,0.,0.)
  p1=Point(1.,0.,0.)
  p2=Point(1.,1.,0.)
  p3=Point(0.,1.,0.)
  l01=Line(p0,p1)
  l12=Line(p1,p2)
  l23=Line(p2,p3)
  l30=Line(p3,p0)
  c=CurveLoop(l01,l12,l23,l30)
  s=PlaneSurface(c)
  d=Design(dim=2,element_size=0.05)
  d.setScriptFileName("quad.geo")
  d.addItems(s)
  pl1=PropertySet("sides",l01,l23)
  pl2=PropertySet("top_and_bottom",l12,l30)
  d.addItems(pl1, pl2)
  dom=MakeDomain(d)
  dom.write("quad.fly")
\end{python}
This example is included with the software in \file{quad.py} in the
\ExampleDirectory.

There are three extra statements which we have not discussed yet.
By default the mesh used to subdivide the boundary is not written into the
mesh file mainly to reduce the size of the data file.
One needs to explicitly add the lines to the \Design which should be present
in the mesh data. Here we additionally labeled the lines on the top and the
bottom with the name ``top_and_bottom`` and the lines on the left and right
hand side with the name ``sides`` using \class{PropertySet} objects.
The labeling is convenient when using tagging\index{tagging}, see \Chap{ESCRIPT CHAP}.

\begin{figure}
\centerline{\includegraphics{quad}}
\caption{Quadrilateral with mesh of triangles}
\label{fig:PYCAD 0}
\end{figure}

If you have \gmshextern installed you can run the example and view the
geometry and mesh with:
\begin{verbatim}
run-escript quad.py
gmsh quad.geo
gmsh quad.msh
\end{verbatim}
See Figure~\ref{fig:PYCAD 0} for a result.

In most cases it is best practice to generate the mesh and solve the
mathematical model in two separate scripts. In our example you can read the
\finley mesh into your simulation code\footnote{\gmshextern files can be
directly read using \function{ReadGmsh}, see \Chap{chap:finley}} using
\begin{python}
  from finley import ReadMesh
  mesh=ReadMesh("quad.fly")
\end{python}
Note that the underlying mesh generation software will not accept all
the geometries you can create.  For example, \pycad will happily allow
you to create a 2-D \class{Design} that is a closed loop with some
additional points or lines lying outside of the enclosed area, but
\gmshextern will fail to create a mesh for it.

\section{Holes}
The example included below shows how to use \pycad to create a 2-D mesh
in the shape of a trapezoid with a cut-out area as in Figure~\ref{fig:PYCAD 1}.

\begin{python}
  from esys.pycad import *
  from esys.pycad.gmsh import Design
  from esys.finley import MakeDomain
  
  # A trapezoid
  p0=Point(0.0, 0.0, 0.0)
  p1=Point(1.0, 0.0, 0.0)
  p2=Point(1.0, 0.5, 0.0)
  p3=Point(0.0, 1.0, 0.0)
  l01=Line(p0, p1)
  l12=Line(p1, p2)
  l23=Line(p2, p3)
  l30=Line(p3, p0)
  c=CurveLoop(l01, l12, l23, l30)
  
  # A small triangular cutout
  x0=Point(0.1, 0.1, 0.0)
  x1=Point(0.5, 0.1, 0.0)
  x2=Point(0.5, 0.2, 0.0)
  x01=Line(x0, x1)
  x12=Line(x1, x2)
  x20=Line(x2, x0)
  cutout=CurveLoop(x01, x12, x20)
  
  # Create the surface with cutout
  s=PlaneSurface(c, holes=[cutout])
  
  # Create a Design which can make the mesh
  d=Design(dim=2, element_size=0.05)
  
  # Add the trapezoid with cutout
  d.addItems(s)
  
  # Create the geometry, mesh and Escript domain
  d.setScriptFileName("trapezoid.geo")
  d.setMeshFileName("trapezoid.msh")
  domain=MakeDomain(d)
  # write mesh to a finley file:
  domain.write("trapezoid.fly")
\end{python}
This example is included with the software in \file{trapezoid.py} in the
\ExampleDirectory.

\begin{figure}
\centerline{\includegraphics{trapezoid}}
\caption{Trapezoid with triangular hole}
\label{fig:PYCAD 1}
\end{figure}

A \code{CurveLoop} is used to connect several lines into a single curve.
It is used in the example above to create the trapezoidal outline for the grid
and also for the triangular cutout area.
You can define any number of lines when creating a \class{CurveLoop}, but
the end of one line must be identical to the start of the next.

\section{A 3D example}

In this section we discuss the definition of 3D geometries. As an example
consider the unit cube as shown in Figure~\ref{fig:PYCAD 2}.
First we generate the vertices of the cube:
\begin{python}
  from esys.pycad import *
  p0=Point(0.,0.,0.)
  p1=Point(1.,0.,0.)
  p2=Point(0.,1.,0.)
  p3=Point(1.,1.,0.)
  p4=Point(0.,0.,1.)
  p5=Point(1.,0.,1.)
  p6=Point(0.,1.,1.)
  p7=Point(1.,1.,1.)
\end{python}

We connect the points to form the bottom and top surfaces of the cube:
\begin{python}
  l01=Line(p0,p1)
  l13=Line(p1,p3)
  l32=Line(p3,p2)
  l20=Line(p2,p0)
  bottom=PlaneSurface(-CurveLoop(l01,l13,l32,l20))
\end{python}

\begin{figure}
\centerline{\includegraphics{brick}}
\caption{Three dimensional block}
\label{fig:PYCAD 2}
\end{figure}

Similar to the definition of a \code{CurvedLoop} the orientation of the
surfaces in a \code{SurfaceLoop} is relevant.
In fact, the surface normal direction defined by the right-hand rule needs to
point outwards as indicated by the surface normals in Figure~\ref{fig:PYCAD 2}.
As the \code{bottom} face is directed upwards it is inserted with the minus
sign into the \code{SurfaceLoop} in order to adjust the orientation of the
surface. Similarly we set 
\begin{python}
  l45=Line(p4,p5)
  l57=Line(p5,p7)
  l76=Line(p7,p6)
  l64=Line(p6,p4)
  top=PlaneSurface(CurveLoop(l45,l57,l76,l64))
\end{python}
To form the front face we introduce the two additional lines connecting the
left and right front points of the \code{top} and \code{bottom} face:
\begin{python}
  l15=Line(p1,p5)
  l40=Line(p4,p0)
\end{python}
To form the front face we encounter a problem, the line \code{l45} used
to define the \code{top} face is pointing the wrong direction.
In \pycad you can reverse the direction of an object by changing its sign.
So we write \code{-l45} to indicate that the direction is to be reversed.
With this notation we can write
\begin{python}
  front=PlaneSurface(CurveLoop(l01,l15,-l45,l40))
\end{python}
Keep in mind that if you use \code{Line(p4,p5)} instead of \code{-l45} both
objects are treated as different although connecting the same points with a
straight line in the same direction. The resulting geometry would include an
opening along the \code{p4}--\code{p5} connection.
This will lead to an inconsistent mesh and may result in a failure of the
volumetric mesh generator. Similarly we can define the other sides of the cube:
\begin{python}
  l37=Line(p3,p7)
  l62=Line(p6,p2)
  back=PlaneSurface(CurveLoop(l32,-l62,-l76,-l37))
  left=PlaneSurface(CurveLoop(-l40,-l64,l62,l20))
  right=PlaneSurface(CurveLoop(-l15,l13,l37,-l57))
\end{python}
We can now put the six surfaces together to form a \class{SurfaceLoop}
defining the boundary of the volume of the cube:
\begin{python}
  sl=SurfaceLoop(top,bottom,front,back,left,right)
  v=Volume(sl)
\end{python}
As in the 2D case, the \class{Design} class is used to define the geometry:
\begin{python}
  from esys.pycad.gmsh import Design
  from esys.finley import MakeDomain

  des=Design(dim=3, element_size = 0.1, keep_files=True)
  des.setScriptFileName("brick.geo")
  des.addItems(v, top, bottom, back, front, left, right)

  dom=MakeDomain(des)
  dom.write("brick.fly")
\end{python}
Note that the \finley mesh file \file{brick.fly} will contain the
triangles used to define the surfaces as they are added to the \class{Design}.
The example script of the cube is included with the software in
\file{brick.py} in the \ExampleDirectory.

\section{Alternative File Formats}
\pycad supports other file formats including I-DEAS universal file, VRML,
Nastran and STL. The following example shows how to generate the STL file
\file{brick.stl}:
\begin{python}
  from esys.pycad.gmsh import Design

  des=Design(dim=3, element_size = 0.1, keep_files=True)
  des.addItems(v, top, bottom, back, front, left , right)

  des.setFileFormat(des.STL)
  des.setMeshFileName("brick.stl")
  des.generate()
\end{python}
The example script of the cube is included with the software in
\file{brick_stl.py} in the \ExampleDirectory.

\section{Element Sizes}
The element size used globally is defined by the \code{element_size} argument
of the \class{Design}. The mesh generator will try to use this mesh size
everywhere in the geometry. In some cases it can be desirable to use a finer
mesh locally. A local refinement can be defined at each \class{Point}:
\begin{python}
  p0=Point(0., 0., 0., local_scale=0.01)
\end{python}
Here the mesh generator will create a mesh with an element size which is by
the factor \code{0.01} times smaller than the global mesh size
\code{element_size=0.3}, see Figure~\ref{fig:PYCAD 5}.
The point where a refinement is defined must be a point on a curve used to
define the geometry.

\begin{figure}
\centerline{\includegraphics{refine}}
\caption{Local refinement at the origin by \var{local_scale=0.01}
with \var{element_size=0.3} and number of elements on the top set to 10}
\label{fig:PYCAD 5}
\end{figure}

Alternatively, one can define a mesh size along a curve by defining the number
of elements to be used to subdivide the curve. For instance, to use $20$
elements on line \code{l23}:
\begin{python}
  l23=Line(p2, p3)
  l23.setElementDistribution(20)
\end{python}
Setting the number of elements on a curve overwrites the global mesh size
\code{element_size}. The result is shown in Figure~\ref{fig:PYCAD 5}.

\section{\pycad Classes}
%\declaremodule{extension}{esys.pycad}
%\modulesynopsis{Python geometry description and meshing interface}

\subsection{Primitives}
Some of the most commonly-used objects in \pycad are listed here.
For a more complete list see the full API documentation.

\begin{classdesc}{Point}{x=0.,y=0.,z=0.\optional{,local_scale=1.}}
creates a point at the given coordinates with local characteristic length \var{local_scale}
\end{classdesc}

\begin{classdesc}{CurveLoop}{list}
creates a closed curve from a \code{list} of
\class{Line}, \class{Arc}, \class{Spline}, \class{BSpline},
\class{BezierSpline} objects.
\end{classdesc}

\begin{classdesc}{SurfaceLoop}{list}
creates a loop of \class{PlaneSurface} or \class{RuledSurface}, which defines
the shell of a volume.
\end{classdesc}

\subsubsection{Lines}
\begin{classdesc}{Line}{point1, point2}
creates a line between two points.
\end{classdesc}

\begin{methoddesc}[Line]{setElementDistribution}{n\optional{,progression=1\optional{,createBump=\False}}}
defines the number of elements on the line. If set, it overwrites the local
length setting which would be applied.
The progression factor \var{progression} defines the change of element size
between neighboured elements. If \var{createBump} is set progression is
applied towards the centre of the line.
\end{methoddesc}

\begin{methoddesc}[Line]{resetElementDistribution}{}
removes a previously set element distribution from the line.
\end{methoddesc}
\begin{methoddesc}[Line]{getElementDistribution}{}
returns the element distribution as a tuple of number of elements, progression
factor and bump flag. If no element distribution is set None is returned.
\end{methoddesc}

\subsubsection{Splines}
\begin{classdesc}{Spline}{point0, point1, ...}
A spline curve defined by a list of points \var{point0}, \var{point1}, \ldots
\end{classdesc}

\begin{methoddesc}[Spline]{setElementDistribution}{n\optional{,progression=1\optional{,createBump=\False}}}
defines the number of elements on the spline. If set, it overwrites the local
length setting which would be applied.
The progression factor \var{progression} defines the change of element size
between neighboured elements. If \var{createBump} is set progression is
applied towards the centre of the spline.
\end{methoddesc}

\begin{methoddesc}[Spline]{resetElementDistribution}{}
removes a previously set element distribution from the spline.
\end{methoddesc}

\begin{methoddesc}[Spline]{getElementDistribution}{}
returns the element distribution as a tuple of number of elements, progression
factor and bump flag. If no element distribution is set None is returned.
\end{methoddesc}

\subsubsection{BSplines}
\begin{classdesc}{BSpline}{point0, point1, ...}
A B-spline curve defined by a list of points \var{point0}, \var{point1}, \ldots
\end{classdesc}

\begin{methoddesc}[BSpline]{setElementDistribution}{n\optional{,progression=1\optional{,createBump=\False}}}
defines the number of elements on the curve. If set, it overwrites the local
length setting which would be applied.
The progression factor \var{progression} defines the change of element size
between neighboured elements. If \var{createBump} is set progression is
applied towards the centre of the curve.
\end{methoddesc}

\begin{methoddesc}[BSpline]{resetElementDistribution}{}
removes a previously set element distribution from the curve.
\end{methoddesc}

\begin{methoddesc}[BSpline]{getElementDistribution}{}
returns the element distribution as a tuple of number of elements, progression
factor and bump flag. If no element distribution is set None is returned.
\end{methoddesc}

\subsubsection{Bezier Curves}
\begin{classdesc}{BezierCurve}{point0, point1, ...}
A Bezier spline curve defined by a list of points \var{point0}, \var{point1}, \ldots
\end{classdesc}

\begin{methoddesc}[BezierCurve]{setElementDistribution}{n\optional{,progression=1\optional{,createBump=\False}}}
defines the number of elements on the curve. If set, it overwrites the local
length setting which would be applied.
The progression factor \var{progression} defines the change of element size
between neighboured elements. If \var{createBump} is set progression is
applied towards the centre of the curve.
\end{methoddesc}

\begin{methoddesc}[BezierCurve]{resetElementDistribution}{}
removes a previously set element distribution from the curve.
\end{methoddesc}

\begin{methoddesc}[BezierCurve]{getElementDistribution}{}
returns the element distribution as a tuple of number of elements, progression
factor and bump flag. If no element distribution is set None is returned.
\end{methoddesc}

\subsubsection{Arcs}
\begin{classdesc}{Arc}{centre_point, start_point, end_point}
creates an arc by specifying a centre for a circle and start and end points.
An arc may subtend an angle of at most $\pi$ radians.
\end{classdesc}

\begin{methoddesc}[Arc]{setElementDistribution}{n\optional{,progression=1\optional{,createBump=\False}}}
defines the number of elements on the arc. If set, it overwrites the local
length setting which would be applied.
The progression factor \var{progression} defines the change of element size
between neighboured elements. If \var{createBump} is set progression is
applied towards the centre of the arc.
\end{methoddesc}

\begin{methoddesc}[Arc]{resetElementDistribution}{}
removes a previously set element distribution from the arc.
\end{methoddesc}

\begin{methoddesc}[Arc]{getElementDistribution}{}
returns the element distribution as a tuple of number of elements, progression
factor and bump flag. If no element distribution is set None is returned.
\end{methoddesc}

\subsubsection{Plane surfaces}
\begin{classdesc}{PlaneSurface}{loop, \optional{holes=[list]}}
creates a plane surface from a \class{CurveLoop}, which may have one or more
holes described by a \var{list} of \class{CurveLoop} objects.
\end{classdesc}

\begin{methoddesc}[PlaneSurface]{setElementDistribution}{n\optional{,progression=1\optional{,createBump=\False}}}
defines the number of elements on all lines.
\end{methoddesc}

\begin{methoddesc}[PlaneSurface]{setRecombination}{\optional{max_deviation=45 * \var{DEG} }}
the mesh generator will try to recombine triangular elements into
quadrilateral elements. \var{max_deviation} (in radians) defines the
maximum deviation of any angle in the quadrilaterals from the right angle.
Set \var{max_deviation}=\var{None} to remove recombination.
\end{methoddesc}

\begin{methoddesc}[PlaneSurface]{setTransfiniteMeshing}{\optional{orientation="Left"}}
applies 2D transfinite meshing to the surface.
\var{orientation} defines the orientation of triangles. Allowed values
are \var{``Left''}, \var{``Right''} and \var{``Alternate''}.
The boundary of the surface must be defined by three or four lines and an
element distribution must be defined on all faces where opposite
faces use the same element distribution. No holes must be present.
\end{methoddesc}

\subsubsection{Ruled Surfaces}
\begin{classdesc}{RuledSurface}{list}
creates a surface that can be interpolated using transfinite interpolation.
\var{list} gives a list of three or four lines defining the boundary of the
surface.
\end{classdesc}

\begin{methoddesc}[RuledSurface]{setRecombination}{\optional{max_deviation=45 * \var{DEG} }}
the mesh generator will try to recombine triangular elements into
quadrilateral elements. \var{max_deviation} (in radians) defines the
maximum deviation of any angle in the quadrilaterals from the right angle.
Set \var{max_deviation}=\var{None} to remove recombination.
\end{methoddesc}

\begin{methoddesc}[RuledSurface]{setTransfiniteMeshing}{\optional{orientation="Left"}}
applies 2D transfinite meshing to the surface.
\var{orientation} defines the orientation of triangles. Allowed values
are \var{``Left''}, \var{``Right''} and \var{``Alternate''}.
The boundary of the surface must be defined by three or four lines and an
element distribution must be defined on all faces where opposite
faces use the same element distribution. No holes must be present.
\end{methoddesc}

\begin{methoddesc}[RuledSurface]{setElementDistribution}{n\optional{,progression=1\optional{,createBump=\False}}}
defines the number of elements on all lines.
\end{methoddesc}

\subsubsection{Volumes}
\begin{classdesc}{Volume}{loop, \optional{holes=[list]}}
creates a volume given a \class{SurfaceLoop}, which may have one or more holes
define by the list of \class{SurfaceLoop}.
\end{classdesc}

\begin{methoddesc}[Volume]{setElementDistribution}{n\optional{,progression=1\optional{,createBump=\False}}}
defines the number of elements on all lines.
\end{methoddesc}

\begin{methoddesc}[Volume]{setRecombination}{\optional{max_deviation=45 * \var{DEG} }}
the mesh generator will try to recombine triangular elements into
quadrilateral elements. These meshes are then used to generate the volume mesh
if possible.
Together with transfinite meshing one can construct rectangular meshes.
\var{max_deviation} (in radians) defines the maximum deviation of any angle in
the quadrilaterals from the right angle.
Set \var{max_deviation}=\var{None} to remove recombination.
\end{methoddesc}

\begin{methoddesc}[Volume]{setTransfiniteMeshing}{\optional{orientation="Left"}}
applies transfinite meshing to the volume and all surfaces (if
\var{orientation} is not equal to \var{None}).
\var{orientation} defines the orientation of triangles. Allowed values
are \var{``Left''}, \var{``Right''} and \var{``Alternate''}.
The boundary of the surface must be defined by three or four lines and an
element distribution must be defined on all faces where opposite
faces use the same element distribution.
If \var{orientation} is equal to \var{None} transfinite meshing is not
switched on for the surfaces but needs to be set by the user.
No holes must be present.
\textbf{Warning: The functionality of transfinite meshing without
recombination is not entirely clear in \gmshextern.
So please apply this method with care.}
\end{methoddesc}

%============================================================================
\subsection{Transformations}

Sometimes it is convenient to create an object and then make copies at
different orientations or in different sizes. This can be achieved by
applying transformations which are used to move geometrical objects in the
3-dimensional space and to resize them.

\begin{classdesc}{Translation}{\optional{b=[0,0,0]}}
defines a translation $x \to x+b$. \var{b} can be any object that can be
converted into a \numpy object of shape $(3,)$.
\end{classdesc}

\begin{classdesc}{Rotation}{\optional{axis=[1,1,1], \optional{ point = [0,0,0], \optional{angle=0*RAD} } } }
defines a rotation by \var{angle} around axis through point \var{point} and direction \var{axis}.
\var{axis} and \var{point} can be any object that can be converted into a
\numpy object of shape $(3,)$.
\var{axis} does not have to be normalised but must have positive length.
The right-hand rule~\cite{RIGHTHANDRULE} applies.
\end{classdesc}

\begin{classdesc}{Dilation}{\optional{factor=1., \optional{centre=[0,0,0]}}}
defines a dilation by the expansion/contraction \var{factor} with
\var{centre} as the dilation centre.
\var{centre} can be any object that can be converted into a \numpy object of
shape $(3,)$.
\end{classdesc}

\begin{classdesc}{Reflection}{\optional{normal=[1,1,1], \optional{offset=0}}}
defines a reflection on a plane defined in normal form $n^t x = d$
where $n$ is the surface normal \var{normal} and $d$ is the plane \var{offset}.
\var{normal} can be any object that can be converted into a \numpy object of
shape $(3,)$.
\var{normal} does not have to be normalised but must have positive length.
\end{classdesc}

\begin{datadesc}{DEG}
a constant to convert from degrees to an internal angle representation in
radians. For instance use \code{90*DEG} for $90$ degrees.
\end{datadesc}

\subsection{Properties}
If you are building a larger geometry you may find it convenient to
create it in smaller pieces and then assemble them.
Property Sets make this easy, and they allow you to name the smaller
pieces for convenience.

Property Sets are used to bundle a set of geometrical objects in a
group. The group is identified by a name. Typically a Property Set
is used to mark subregions which share the same material properties or
to mark portions of the boundary. For efficiency, the \Design class
assigns an integer to each of its Property Sets, a so-called tag\index{tag}.
The appropriate tag is attached to the elements at generation time.

See the file \code{pycad/examples/quad.py} for an example using a {\it PropertySet}.

\begin{classdesc}{PropertySet}{name,*items}
defines a group geometrical objects which can be accessed through a \var{name}
The objects in the tuple \var{items} mast all be \ManifoldOneD, \ManifoldTwoD or \ManifoldThreeD objects.
\end{classdesc}

\begin{methoddesc}[PropertySet]{getManifoldClass}{}
returns the manifold class \ManifoldOneD, \ManifoldTwoD or \ManifoldThreeD
expected from the items in the property set.
\end{methoddesc}

\begin{methoddesc}[PropertySet]{getDim}{}
returns the spatial dimension of the items in the property set.
\end{methoddesc}

\begin{methoddesc}[PropertySet]{getName}{}
returns the name of the set
\end{methoddesc}

\begin{methoddesc}[PropertySet]{setName}{name}
sets the name. This name should be unique within a \Design.
\end{methoddesc}

\begin{methoddesc}[PropertySet]{addItem}{*items}
adds a tuple of items. They need to be objects of class \ManifoldOneD,
\ManifoldTwoD or \ManifoldThreeD.
\end{methoddesc}

\begin{methoddesc}[PropertySet]{getItems}{}
returns the list of items
\end{methoddesc}

\begin{methoddesc}[PropertySet]{clearItems}{}
clears the list of items
\end{methoddesc}

\begin{methoddesc}[PropertySet]{getTag}{}
returns the tag used for this property set
\end{methoddesc}

\section{Interface to the mesh generation software}
%\declaremodule{extension}{esys.pycad.gmsh}
%\modulesynopsis{Python geometry description and meshing interface}

The class and methods described here provide an interface to the mesh
generation software, which is currently \gmshextern. This interface could be
adopted to \emph{triangle} or another mesh generation package if this is
deemed to be desirable in the future.

\begin{classdesc}{Design}{
\optional{dim=3, \optional{element_size=1., \optional{order=1, \optional{keep_files=False}}}}}
describes the geometry defined by primitives to be meshed.
\var{dim} specifies the spatial dimension, while \var{element_size} defines
the global element size which is multiplied by the local scale to set the
element size at each \Point.
The argument \var{order} defines the element order to be used.
If \var{keep_files} is set to \True temporary files are kept, otherwise they
are removed when the instance of the class is deleted.
\end{classdesc}

\begin{memberdesc}[Design]{GMSH}
gmsh file format~\cite{GMSH}
\end{memberdesc}

\begin{memberdesc}[Design]{IDEAS}
I-DEAS universal file format~\cite{IDEAS}
\end{memberdesc}

\begin{memberdesc}[Design]{VRML}
VRML file format, \cite{VRML}
\end{memberdesc}

\begin{memberdesc}[Design]{STL}
STL file format~\cite{STL}
\end{memberdesc}

\begin{memberdesc}[Design]{NASTRAN}
NASTRAN bulk data format~\cite{NASTRAN}
\end{memberdesc}

\begin{memberdesc}[Design]{MEDIT}
Medit file format~\cite{MEDIT}
\end{memberdesc}

\begin{memberdesc}[Design]{CGNS}
CGNS file format~\cite{CGNS}
\end{memberdesc}

\begin{memberdesc}[Design]{PLOT3D}
Plot3D file format~\cite{PLOT3D}
\end{memberdesc}

\begin{memberdesc}[Design]{DIFFPACK}
Diffpack 3D file format~\cite{DIFFPACK}
\end{memberdesc}

\begin{memberdesc}[Design]{DELAUNAY}
the Delaunay triangulator, see \gmshextern and \cite{TETGEN}
\end{memberdesc}

\begin{memberdesc}[Design]{MESHADAPT}
the gmsh triangulator, see \gmshextern
\end{memberdesc}

\begin{memberdesc}[Design]{FRONTAL}
the NETGEN~\cite{NETGEN} triangulator
\end{memberdesc}

\begin{methoddesc}[Design]{generate}{}
generates the mesh file. The data are written to the file \var{Design.getMeshFileName}.
\end{methoddesc}

\begin{methoddesc}[Design]{setDim}{\optional{dim=3}}
sets the spatial dimension which needs to be $1$, $2$ or $3$.
\end{methoddesc}

\begin{methoddesc}[Design]{getDim}{}
returns the spatial dimension.
\end{methoddesc}

\begin{methoddesc}[Design]{setElementOrder}{\optional{order=1}}
sets the element order which needs to be $1$ or $2$.
\end{methoddesc}

\begin{methoddesc}[Design]{getElementOrder}{}
returns the element order.
\end{methoddesc}

\begin{methoddesc}[Design]{setElementSize}{\optional{element_size=1}}
sets the global element size. The local element size at a point is defined as
the global element size multiplied by the local scale.
The element size must be positive.
\end{methoddesc}

\begin{methoddesc}[Design]{getElementSize}{}
returns the global element size.
\end{methoddesc}

\begin{methoddesc}[Design]{setKeepFilesOn}{}
work files are kept at the end of the generation.
\end{methoddesc}

\begin{methoddesc}[Design]{setKeepFilesOff}{}
work files are deleted at the end of the generation.
\end{methoddesc}

\begin{methoddesc}[Design]{keepFiles}{}
returns \True if work files are kept, \False otherwise.
\end{methoddesc}

\begin{methoddesc}[Design]{setScriptFileName}{\optional{name=None}}
sets the file name for the gmsh input script.
If no name is given a name with extension "geo" is generated.
\end{methoddesc}

\begin{methoddesc}[Design]{getScriptFileName}{}
returns the name of the file for the gmsh script.
\end{methoddesc}

\begin{methoddesc}[Design]{setMeshFileName}{\optional{name=None}}
sets the name for the mesh file. If no name is given a name is generated.
The format is set by \\\var{Design.setFileFormat}.
\end{methoddesc}

\begin{methoddesc}[Design]{getMeshFileName}{}
returns the name of the mesh file.
\end{methoddesc}

\begin{methoddesc}[Design]{addItems}{*items}
adds the tuple of \var{items}. An item can be any primitive or a
\class{PropertySet}.
\warning{If a \PropertySet is added which includes an object that is not
part of a \PropertySet, it may not be considered in the meshing.}
\end{methoddesc}

\begin{methoddesc}[Design]{getItems}{}
returns a list of the items.
\end{methoddesc}

\begin{methoddesc}[Design]{clearItems}{}
resets the items in this design.
\end{methoddesc}

\begin{methoddesc}[Design]{getMeshHandler}{}
returns a handle to the mesh.
Calling this method generates the mesh from the geometry and returns a
mechanism to access the mesh data. In the current implementation this
method returns a file name for a file containing the mesh data.
\end{methoddesc}

\begin{methoddesc}[Design]{getScriptString}{}
returns the gmsh script to generate the mesh as a string.
\end{methoddesc}

\begin{methoddesc}[Design]{getCommandString}{}
returns the gmsh command used to generate the mesh as a string.
\end{methoddesc}

\begin{methoddesc}[Design]{setOptions}{
\optional{algorithm=None
\optional{, optimize_quality=\True
\optional{, smoothing=1
\optional{, curvature_based_element_size=\False
\optional{, algorithm2D=None
\optional{, algorithm3D=None
\optional{, generate_hexahedra=False
\optional{, random_factor=None}}}}}}}}}
sets options for the mesh generator.
Both \var{algorithm} and \var{algorithm2D} set the 2D meshing algorithm to be
used. If both parameters are given, they must be equal.
The algorithm needs to be \var{Design.DELAUNAY}, \var{Design.FRONTAL},
or \var{Design.MESHADAPT}. By default \var{Design.MESHADAPT} is used.
\var{algorithm3D} sets the 3D  meshing algorithm to be used.
The algorithm needs to be \var{Design.DELAUNAY} or \var{Design.FRONTAL}.
By default \var{Design.FRONTAL} is used.
\var{optimize_quality}=\True invokes an optimization of the mesh quality.
\var{smoothing} sets the number of smoothing steps to be applied to the mesh.
\var{curvature_based_element_size}=\True switches on curvature based
definition of element size.
\var{generate_hexahedra}=\True switches on the usage of quadrilateral or
hexahedral elements.
\var{random_factor} a positive amount used in the 2D meshing algorithm.
\end{methoddesc}

\begin{methoddesc}[Design]{getTagMap}{}
returns a \class{TagMap} to map the \class{PropertySet} names to tag numbers
generated by gmsh.
\end{methoddesc}

\begin{methoddesc}[Design]{setFileFormat}{\optional{format=\var{Design.GMSH}}}
sets the file format. \var{format} must be one of the values:\\
\var{Design.GMSH}\\
\var{Design.IDEAS}\\
\var{Design.VRML}\\
\var{Design.STL}\\
\var{Design.NASTRAN}\\
\var{Design.MEDIT}\\
\var{Design.CGNS}\\
\var{Design.PLOT3D}\\
\var{Design.DIFFPACK}.
\end{methoddesc}

\begin{methoddesc}[Design]{getFileFormat}{}
returns the file format.
\end{methoddesc}