-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCH_coding.xml
1798 lines (1721 loc) · 71.9 KB
/
CH_coding.xml
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
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!--
This software was produced by NIST, an agency of the
U.S. government, and by statute is not subject to copyright in the
United States. Recipients of this software assume all
responsibilities associated with its operation, modification and
maintenance. However, to facilitate maintenance we ask that before
distributing modified versions of this software, you first contact
the authors at [email protected].
-->
<chapter id="Chapter-Coding">
<title>Writing &oof2; Extensions</title>
<para>
This chapter describes the contents of the source files for &oof2;
extensions. It begins with an overview of some import
classes. <xref linkend="Chapter-Extending"/> describes how to
assemble the source files into a loadable extension module.
</para>
<section id="Section-Coding-Directory-Structure">
<title>Source Directory Layout</title>
<para>
The &oof2; source code is divided into four main directories:
<itemizedlist>
<listitem>
<simpara>
<filename>SRC/common</filename>: µs; and general
infrastructure.
</simpara>
</listitem>
<listitem>
<simpara>
<filename>SRC/engine</filename>: &skels;, &meshes;,
&properties;, &materials;, and anything else related to the
finite element machinery.
</simpara>
</listitem>
<listitem>
<simpara>
<filename>SRC/image</filename>: &images;.
</simpara>
</listitem>
<listitem>
<simpara>
<filename>SRC/orientationmap</filename>: <link
linkend="Section-Concepts-Microstructure-OrientationMap">orientation
maps</link>.
</simpara>
</listitem>
</itemizedlist>
Most of these directories have a subdirectory called
<filename>IO</filename> for files related to user interactions
(menus and such). Inside <filename>IO</filename> a directory
called <filename>GUI</filename> holds the gtk-specific code for
the graphical user interface. It's not always obvious what
belongs in <filename>IO</filename> and what doesn't, so we
regretted making that distinction, and the most recent
subdirectory, <filename>SRC/orientationmap</filename>, skips
<filename>IO</filename> and just contains
<filename>GUI</filename> directly.
</para>
<para>
As a general rule, files in <filename>SRC/common</filename>
don't include or import any files in any other
subdirectories. Files in <filename>SRC/engine</filename>,
<filename>SRC/image</filename>, and
<filename>SRC/orientationmap</filename> include or import
code from <filename>SRC/common</filename> but from nowhere else.
Similarly, all of the code in any <filename>GUI</filename>
directory refers to code in the directories above it, but
nothing in the directories above refers to anything in
<filename>GUI</filename>. This ensures that the program will
work correctly when not using the GUI.<footnote><simpara>The
only exception to these rules is that the code for starting the
GUI and loading the various modules, in
<filename>SRC/common/oof.py</filename>, needs to be able to
initialize the other modules.</simpara></footnote>
</para>
<para>
Additional directories within <filename>SRC</filename> don't
have <filename>IO</filename> or <filename>GUI</filename>
subdirectories. They are
<itemizedlist>
<listitem>
<simpara>
<filename>EXTENSIONS</filename>: Source code for
internal extensions.
</simpara>
</listitem>
<listitem>
<simpara>
<filename>Eigen</filename> and
<filename>unsupported</filename>: a copy of the <ulink
url="https://eigen.tuxfamily.org/"
role="external">Eigen</ulink> library and its unsupported
extensions.
</simpara>
</listitem>
<listitem>
<simpara>
<filename>tutorials</filename>: the &oof2; <link
linkend="MenuItem-OOF.Help.Tutorials">tutorials</link>.
</simpara>
</listitem>
</itemizedlist>
Of these, only <filename>EXTENSIONS</filename> is important to
extension authors, probably.
</para>
</section>
<section id="Section-Coding-Package">
<title>Python Package Layout</title>
<para>
The Python package structure mimics the directory layout
described in <xref
linkend="Section-Coding-Directory-Structure"/>. The top-level
package is <code>ooflib</code>, and within it are packages
called <code>common</code>, <code>engine</code>,
<code>image</code>, and <code>orientationmap</code>. When &oof2;
is started, it modifies the Python path so that the
<code>ooflib</code> package can be found.
</para>
<para>
Python modules created by <ulink url="http://www.swig.org"
role="external">swig</ulink>, however, are installed into the
package <code>ooflib.SWIG</code>. Within
<code>ooflib.SWIG</code> are packages named <code>common</code>,
<code>engine</code>, <code>image</code>, and
<code>orientationmap</code>.
</para>
<para>
For example, the Python code defining the
<classname>Microstructure</classname>
<filename>SRC/common/microstructure.py</filename> can be
imported into another Python file with
<programlisting>
from ooflib.common import microstructure </programlisting>
and the C++ underpinnings of the class, defined in
<filename>SRC/common/cmicrostructure.C</filename> and wrapped by
<filename>SRC/common/cmicrostructure.swg</filename>, can be
imported with
<programlisting>
from ooflib.SWIG.common import cmicrostructure </programlisting>
</para>
</section>
<!-- =========================================================== -->
<section id="Section-Coding-AddFields">
<title>Adding New Fields, Fluxes, and Equations</title>
<para>
New &Fields;, &Fluxes;, and &Equations; can be
added with just a few lines of Python code. The &oof2;
&Field;, &Flux;, and &Equation; classes represent
<emphasis>global</emphasis> objects — there is only one
instance of the <varname>Temperature</varname> field, for
example, although the field may be defined on more than one
&mesh;, or on none at all. Creating a &Field;,
&Flux;, or &Equation; object makes it available for
use in a &mesh;, and records some information about it, such as
its name and dimension.
</para>
<para>
When a new material &Property; is created, it indicates which
&Fields;, &Fluxes; and &Equations; it uses. The &Field;,
&Flux;, and &Equation; classes themselves are not explicitly
tied to any particular &Properties;. See <xref
linkend="Section-Coding-Material"/> for the details.
</para>
<para>
&oof2; predefines some &Fields;, &Fluxes;, and
&Equations; in <filename>SRC/engine/problem.py</filename>.
Refer to that file for examples.
</para>
<para>
The &Field; subclasses defined in
<code>ooflib.SWIG.engine.field</code> are:
<itemizedlist spacing="compact">
<listitem>
<simpara>
<xref linkend="Class-ScalarField"/>:
<foreignphrase>e.g,</foreignphrase>, Temperature, Density
</simpara>
</listitem>
<listitem>
<simpara>
<xref linkend="Class-TwoVectorField"/>:
<foreignphrase>e.g,</foreignphrase> Displacement,
Polarization
</simpara>
</listitem>
</itemizedlist>
These classes actually define <xref
linkend="Class-CompoundField"/>s, which represent the <link
linkend="Section-Concepts-Mesh-3D">in-plane</link> part of a
&Field; along with its out of plane components, and the time
derivatives of its in-plane and out-of-plane components.
</para>
<para>
To create a &Field;, &Flux;, or &Equation;,
simply call the derived class's Python
constructor.<footnote><para> &Fields;, &Fluxes;, and
&Equations; are C++ classes, but they should only be
instantiated by calling their swigged Python
constructors. </para></footnote> For example:
<programlisting>
from ooflib.SWIG.engine import field
Temperature = field.ScalarField("Temperature") </programlisting>
</para>
<para>
New &Fields; should only be created by calling <xref
linkend="Class-CompoundField"/> constructors in Python. This
will create four new Python variables whose names are the name
of the &Field;, the name of the field suffixed with
<quote>_z</quote> for the out-of-plane field, and the name
suffixed with <quote>_t</quote> and <quote>_tz</quote> for the
time derivative and its out-of-plane part. These variables live
in the main &oof2; namespace, which is the one in which <link
linkend="Section-ScriptGUI">text mode</link>, <link
linkend="MenuItem-OOF.File.Load.Script">scripts</link> and <link
linkend="Section-Windows-Console">Console Window</link> commands
are executed.
</para>
<para>
Code outside of the main &oof2; namespace can gain access to the
new &Fields; with the <xref linkend="Function-getField"/>
function. Within the script that created the &Field;, the
&Field; can of course be referred to via a local variable
referring to the new object:
<programlisting>
<emphasis role="bold">gee</emphasis> = field.TwoVectorField("<emphasis role="bold">gee</emphasis>")</programlisting>
It is acceptable for the local variable name to be the same as
the global variable name.
</para>
<para>
The &Flux; subclasses defined in
<code>ooflib.SWIG.engine.flux</code> are:
<itemizedlist spacing="compact">
<listitem>
<simpara>
<xref linkend="Class-VectorFlux"/>:
<foreignphrase>e.g,</foreignphrase> Mass Current, Heat
Flow
</simpara>
</listitem>
<listitem>
<simpara>
<xref linkend="Class-SymmetricTensorFlux"/>:
<foreignphrase>e.g,</foreignphrase> Stress
</simpara>
</listitem>
</itemizedlist>
</para>
<para>
The &Equation; subclasses defined in
<code>ooflib.SWIG.engine.equation</code> are:
<itemizedlist spacing="compact">
<listitem>
<simpara>
<xref linkend="Class-DivergenceEquation"/>: any
equation of the form <quote>divergence of a &Flux; =
external force</quote>.
</simpara>
</listitem>
<listitem>
<simpara>
<xref linkend="Class-PlaneFluxEquation"/>: any equation
that constrains the out-of-plane components of a &Flux;.
</simpara>
</listitem>
</itemizedlist>
The &Equation; constructors each take a name, a &Flux;, and a
dimension. The &Flux; is the
&Flux; that the &Equation; operates on, and the dimension is an
integer specifying how many components the equation has. (The
divergence of a vector &Flux; has one component, and the divergence
of a tensor flux has three components. A
<classname>PlaneFluxEquation</classname> has as many components
as there are out-of-plane components of the associated &Flux;.)
</para>
<para>
Like the <xref linkend="Class-Field"/> classes,
<xref linkend="Class-Flux"/>es and
<xref linkend="Class-Equation"/>s can be retrieved by name in the
main &oof2; namespace. They can also be retrieved by the
<xref linkend="Function-getFlux"/> and
<xref linkend="Function-getEquation"/> functions in either C++ or
Python. Unlike <xref linkend="Class-Field"/>s, they have no
auxiliary out-of-plane or time derivative parts.
</para>
<para>
As an example, the following code fully defines the quantities
necessary to solve the static heat conductivity problem (except
for the thermal conductivity &material; &property;, which is
discussed <link linkend="Section-Coding-Material">later</link>).
<programlisting>
from ooflib.SWIG.engine.field import ScalarField
from ooflib.SWIG.engine.flux import VectorFlux
from ooflib.SWIG.engine.equation import DivergenceEquation, PlaneFluxEquation
Temperature = ScalarField('Temperature')
Heat_Flux = VectorFlux('Heat_Flux')
HeatBalanceEqn = DivergenceEquation('Heat_Eqn', Heat_Flux, 1)
HeatOutOfPlane = PlaneFluxEquation('Plane_Heat_Flux', Heat_Flux, 1) </programlisting>
Note that all of these quantities are already defined in &oof2;
(in <filename>SRC/engine/problem.py</filename> to be exact) so
don't redefine them. This is just an example of how you might
define new &Fields; and &Equations;.
</para>
</section><!-- Adding New Fields, Fluxes, and Equations -->
<!-- ========================================================== -->
<section id="Section-Coding-Indices">
<title>Indices and Iterators</title>
<para>
Before proceeding, it's necessary to take a diversion on the
topic of indices and iterators.
</para>
<para>
It is often necessary to refer to the components of &Fields;,
&Fluxes;, &Equations;, and &OutputVal;s. &oof2;'s
<filename>fieldindex</filename> module provides a generic
mechanism for doing this, so that it's possible to loop over all
of the components of an object without even having to know how
many indices are required to specify a component. It takes two
indices to specify a tensor component, one for a vector, and
zero for a scalar, but all three cases can be handled
identically.
</para>
<para>
The basic machinery contains:
<itemizedlist>
<listitem>
<para>
The &FieldIndex; base class, which designates a component
of a &Field; or other object. There are different
subclasses for different kinds of objects (scalars,
vectors, tensors, <foreignphrase>etc</foreignphrase>).
</para>
</listitem>
<listitem>
<para>
A &Components; base class, which is a container that can
be iterated over to obtain the &FieldIndexes;. There are
different subclasses for different kinds of objects, and
for different ways of iterating over them (in-plane,
out-of-plane, <foreignphrase>etc</foreignphrase>). Each
indexable class (&Field;, &Flux;,
<foreignphrase>etc</foreignphrase>) has a virtual function
that returns the appropriate type of &Components;.
</para>
</listitem>
<listitem>
<para>
A &ComponentIterator; base class that is used for
iterating over the &Components;. Different subclasses of
&ComponentIterator;s iterate over different types of
&Components;.
</para>
</listitem>
<listitem>
<para>
Generic wrapper classes, &IndexP; and
&ComponentIteratorP;, that wrap pointers to &FieldIndex;
and &ComponentIterator;.
</para>
</listitem>
</itemizedlist>
</para>
<para>
The base class for indices is &FieldIndex;. There are subclasses
of &FieldIndex; for different kinds of &Fields;, &Fluxes;,
&Equations;, and &OutputVal;s. (From now on we'll talk about
&Fields;, but everything also applies to &Fluxes;, &Equations;
and &OutputVal;s, unless explicitly stated otherwise.) To
handle the polymorphism, generic C++ code doesn't use the
&FieldIndex; classes directly. Instead it uses a wrapper class,
&IndexP;, which is a light-weight object that contains a pointer
(hence the <quote>P</quote> in the name) to a &FieldIndex;. The
wrapper frees the user from having to know the actual class
being used. It provides access to its &FieldIndex; and its
virtual functions, and deallocates it when it goes out of scope.
</para>
<para>
Note that &IndexP; does not do reference
counting. When an &IndexP; is destructed,
its &FieldIndex; is destructed as well.
When an &IndexP; is copied, the original
&FieldIndex; is copied as well.
</para>
<para>
For looping over the indices of a &Field;, there are
&Components; and &ComponentIterator; class hierarchies. Each
type of &Field; has a <link
linkend="Class-Field-components"><function>components()</function></link>
method that returns a &Components; pointer. The &Components; classes
are conceptually containers for &FieldIndexes;. The instances
of the &Components; subclasses are static immutable objects.
</para>
<para>
In C++, &Components; objects have
<function>begin()</function> and <function>end()</function>
methods that return &ComponentIterator;
objects, allowing them to work like STL iterators for looping
over indexes. For example, you can print the indices of the
components of the Temperature and Displacement &Fields; like this:
<programlisting>
Field *temperature = Field::getField("Temperature");
Components* comps = temperature->components();
for(ComponentIteratorP ip = comps->begin(); ip!=comps->end(); ++ip)
std::cerr << *ip << std::endl;
Field *displacement = Field::getField("Displacement");
Components* comps = displacement->components();
for(ComponentIteratorP ip = comps->begin(); ip!=comps->end(); ++ip)
std::cerr << *ip << std::endl; </programlisting>
or, more compactly, using range based for loops like this:
<programlisting>
for(IndexP i : *Field::getField("Temperature")->components())
std::cerr << i << std::endl;
for(IndexP i : *Field::getField("Displacement")->components())
std::cerr << i << std::endl; </programlisting>
both of which print
<literallayout class="monospaced">
IndexP(ScalarFieldIndex()) <lineannotation>[The single component of Temperature]</lineannotation>
IndexP(VectorFieldIndex(0)) <lineannotation>[The first component of Displacement]</lineannotation>
IndexP(VectorFieldIndex(1)) <lineannotation>[The second component of Displacement]</lineannotation></literallayout>
</para>
<para>
In Python, the <function>components()</function>
methods return generator functions that serve the same purpose.
For example, in Python you can print the indices of the
components of the temperature and displacement &Fields; like
this:
<programlisting>
for i in Temperature.components():
print(i)
for i in Displacement.components():
print(i) </programlisting>
which prints
<literallayout class="monospaced">
ScalarFieldIndex() <lineannotation>[The single component of Temperature]</lineannotation>
VectorFieldIndex(0) <lineannotation>[The first component of Displacement]</lineannotation>
VectorFieldIndex(1) <lineannotation>[The second component of Displacement]</lineannotation></literallayout>
</para>
<para>
In some cases it's necessary to restrict iteration to either the
<link linkend="Section-Concepts-Mesh-3D">in-plane or
out-of-plane components</link> of a &Field; or &Flux; (but not
an &Equation;). In those cases, the
<methodname>components()</methodname> method takes an argument
of the &Planarity; class. If no argument is provided, as in the
examples above, the default planarity is
<varname>ALL_INDICES</varname>. See <link
linkend="Class-Field-components"><function>Field::components</function></link>,
<link
linkend="Class-Field-oop-components"><function>Field::outOfPlaneComponents</function></link>,
<link
linkend="Class-Flux-components"><function>Flux::components</function></link>,
and <link
linkend="Class-Flux-oop-components"><function>Flux::outOfPlaneComponents</function></link>
for examples and some important details.
</para>
</section> <!-- Indices and Iterators -->
<section id="Section-Coding-Conjugates">
<title>Conjugate Pairs</title>
<para>
Finite element problems which lead to a symmetric stiffness
matrix can be solved efficiently by the <link
linkend="RegisteredClass-ConjugateGradient">conjugate
gradient</link> method. &oof2; needs to be told how to construct
a symmetric matrix, though.
</para>
<para>
Consider a simple elasticity problem. The degrees of freedom
are the components of the displacement at each node of the mesh.
The equations are the components of the force balance at each
node. In the stiffness matrix, degrees of freedom correspond to
columns and equations correspond to rows. The matrix will be
symmetric if the columns and rows are ordered so that the force
component and node of the
<emphasis>n</emphasis><superscript>th</superscript> row are the
same as the displacement component and node of the
<emphasis>n</emphasis><superscript>th</superscript> column.
</para>
<para>
In general, this correspondence is <emphasis>not</emphasis>
trivial. If the stress in the example above came from material
properties other than elasticity, the matrix would not
necessarily be symmetric. Including thermal expansion can make
an elasticity problem asymmetric.
</para>
<para>
The function <link
linkend="Function-conjugatePair"><function>conjugatePair()</function></link>
in the <code>ooflib.SWIG.engine.conjugate</code> module
establishes the correspondence between &Field; and &Equation;
components that is necessary to build a symmetric matrix. After
<link linkend="Section-Coding-AddFields">creating</link>
&Fields; and &Equations;, call
<function>conjugatePair</function> like this:
<programlisting>
from ooflib.SWIG.engine.conjugate import conjugatePair
conjugatePair(proptype, equation, eqncomp, field, fieldcomp) </programlisting>
where <varname>proptype</varname> is the <link
linkend="para-propertyType"><classname>PropertyType</classname></link>
of the &property; that will be symmetrized by this conjugacy
pair; <varname>equation</varname> is an &Equation; object;
<varname>field</varname> is a &Field; object, and
<varname>eqncomp</varname> and <varname>fieldcomp</varname> are
either &FieldIndex; objects or iterable containers of them. For
example, the following code establishes the correspondence
between the <emphasis>x</emphasis> and <emphasis>y</emphasis>
components of the displacement field and the force balance
equation (<code>import</code> statements have been omitted for
conciseness):
<programlisting>
equation = getEquation('Force_Balance')
field = getField('Displacement')
conjugatePair("Elasticity",
equation, equation.components(),
field, field.components()) </programlisting>
See <link
linkend="Function-conjugatePair"><function>conjugatePair()</function></link>
for some important details about making sure that the &Equation;
and &Field; components are in the same order.
</para>
<para>
Calling <function>conjugatePair</function> is necessary but not
sufficient for &oof2; to create symmetric stiffness matrices.
The &properties; that contribute to the stiffness matrix must
also support symmetry. This is discussed in <xref
linkend="Section-Coding-Material"/>.
</para>
<para>
It is not required that <function>conjugatePair</function> be
called on all defined fields and equations, but failing to do so
will make the <link
linkend="RegisteredClass-ConjugateGradient">conjugate
gradient</link> solver unavailable for problems involving these
fields and equations.
</para>
<para>
It is also not necessary to call the
<function>conjugatePair</function> for the predefined fields in
&oof2;, since conjugacy is predefined for these fields and
equations in <filename>SRC/engine/problem.py</filename>.
</para>
</section><!-- Conjugate Pairs -->
<section id="Section-Coding-Material">
<title>Adding New Material Properties</title>
<para>
New &material; &properties; are the most complicated kind of
&oof2; extension. Just to make it simpler, there are two ways
of doing it. &properties; can be written in C++ or they can be
written in Python. Python &properties; are a bit easier to
write and install, but will run significantly more slowly. It
may be convenient to develop new &properties; in Python and
translate them to C++ after the bugs have been worked out.
</para>
<para>
Whether a &property; is written in C++ or Python, the same code
elements must be present. Not all of them are necessary for
every &property;. It is safe simply to omit the unnecessary
ones.
</para>
<para>
A &property; consists of a class definition and a <link
linkend="Class-PropertyRegistration"><classname>PropertyRegistration</classname></link>
object. The registration contains metadata about the &property;
and allows it to be found in the user interface. The class
must be a subclass of one of these intermediate base classes:
<itemizedlist>
<listitem>
<para>
A <xref linkend="Class-FluxProperty"/> contributes to a
&flux;. For example, <xref
linkend="Property-Mechanical-Elasticity-Isotropic"/>
contributes to <xref linkend="Flux-Stress"/>.
</para>
</listitem>
<listitem>
<para>
An <xref linkend="Class-EqnProperty"/> contributes
directly to an &equation;. For example, <xref
linkend="Property-Mechanical-MassDensity-ConstantMassDensity"/>
provides the mass times acceleration term in the <xref
linkend="Equation-Force_Balance"/> equation.
</para>
</listitem>
<listitem>
<para>
An <xref linkend="Class-AuxProperty"/> is neither of the
above, but can be used indirectly by other &Properties;.
For example, <xref linkend="Property-Orientation"/> is
used by all anisotropic &Properties;. <xref
linkend="Property-Color"/> is used to display &materials;.
</para>
</listitem>
</itemizedlist>
See the documentation for each intermediate class for the
details.
</para>
<para>
&property; classes perform the following types of tasks (the
links below each task lead to detailed documentation, see the
pages for the classes for the full list of methods):
<itemizedlist>
<!-- TODO: Fix links. Some links to go to Property methods
that are now in PhysicalProperty or more derived classes
-->
<listitem>
<para>
<anchor id="para-propertyType"/>
<emphasis>Identification.</emphasis> &properties; have
names by which they are identified in the user interface.
They also have a <emphasis>propertyType</emphasis>. A
<varname>propertyType</varname> is a string that
identifies the &property;'s physical role. Each
&material; instance can have at most one &property; of
each <varname>propertyType</varname>. For example, all
&properties; that provide an elastic modulus have the type
<literal>'Elasticity'</literal>, and all thermal expansion
&properties; have the type
<literal>'ThermalExpansion'</literal>. Authors of new
types of &property; should invent new
<varname>propertyType</varname> strings. Authors of new
versions of a preexisting &property; should examine the
existing <link
linkend="Class-PropertyRegistration"><classname>PropertyRegistrations</classname></link>
and reuse the same <varname>propertyType</varname>.
</para>
<itemizedlist spacing="compact">
<listitem>
<simpara>
<link linkend="Class-PropertyRegistration"><classname>PropertyRegistration</classname></link>
</simpara>
</listitem>
<listitem>
<simpara><link
linkend="Class-Property-Constructors"><classname>Property</classname>
constructor</link></simpara>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
<emphasis>Cross Referencing.</emphasis> Sometimes a
&property; needs to use data from other &properties; in
the same &material;. For example, any anisotropic
&property; will need to find out its &material;'s <link
linkend="MenuItem-OOF.Property.Parametrize.Orientation"><classname>Orientation</classname></link>. Cross
referencing allows a &property; to use
<classname>propertyType</classname> information to locate
other &properties;. This is done after a &material; is
constructed but before it is used for numerical
computations.
</para>
<itemizedlist spacing="compact">
<listitem>
<simpara><link
linkend="Class-Property-cross_reference"><code>Property::cross_reference</code></link></simpara>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
<anchor id="para-precomputation"/>
<emphasis>Precomputation.</emphasis> When constructing the
finite element stiffness matrix, the &oof2; program loops
over all &elems; and asks their &materials; to ask their
&properties; to make their contributions to the matrix.
For efficiency, &properties; may want to precompute some
quantities before this process begins. There are two
possible precomputation points: &elem;- and
&node;-independent computations that are done before
looping over &elems;, and
&elem;-<emphasis>de</emphasis>pendent ones that are done
before looping over &nodes;.
</para>
<itemizedlist spacing="compact">
<listitem>
<simpara>
<link
linkend="Class-Property-precompute"><code>Property::precompute</code></link>
</simpara>
</listitem>
<listitem>
<simpara>
<link
linkend="Class-Property-begin_element"><code>Property::begin_element</code></link>
</simpara>
</listitem>
<listitem>
<simpara>
<link
linkend="Class-FluxProperty-begin_point"><code>FluxProperty::begin_point</code></link>
and <link linkend="Class-EqnProperty-begin_point"><code>EqnProperty::begin_point</code></link>
</simpara>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
<anchor id="para-computation"/>
<!-- TODO update this -->
<emphasis>Computation.</emphasis> &properties; can make
contributions to the finite element stiffness matrix and
to the right-hand side vector of external
<quote>forces</quote>.
</para>
<itemizedlist spacing="compact">
<listitem>
<simpara>
<link
linkend="Class-FluxProperty-flux_matrix"><code>FluxProperty::flux_matrix</code></link>
</simpara>
</listitem>
<listitem>
<simpara>
<link
linkend="Class-FluxProperty-flux_offset"><code>FluxProperty::flux_offset</code></link>
</simpara>
</listitem>
</itemizedlist>
<para>
There are also a few functions that &properties; may
define that influence the way they're used or the way a
computation is done:
</para>
<itemizedlist spacing="compact">
<listitem>
<simpara>
<link
linkend="Class-Property-is_symmetric"><code>Property::is_symmetric</code></link>
</simpara>
</listitem>
<listitem>
<simpara>
<link
linkend="Class-PhysicalProperty-integration_order"><code>PhysicalProperty::integration_order</code></link>
</simpara>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
<emphasis>Postcomputation.</emphasis> This is just like
<link linkend="para-precomputation">precomputation</link>,
but later. It can be done either after the <link
linkend="para-computation">computation</link> on each
&elem;, or after a full solution has been obtained.
</para>
<itemizedlist spacing="compact">
<listitem>
<simpara>
<link
linkend="Class-FluxProperty-end_point"><code>FluxProperty::end_point</code></link>
and
<link linkend="Class-EqnProperty-end_point"><code>EqnProperty::end_point</code></link>
</simpara>
</listitem>
<listitem>
<simpara>
<link
linkend="Class-Property-end_element"><code>Property::end_element</code></link>
</simpara>
</listitem>
<listitem>
<simpara>
<link
linkend="Class-Property-post_process"><code>Property::post_process</code></link>
</simpara>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
<emphasis>Outputs.</emphasis> Many <link
linkend="Section-Concepts-Outputs">output</link>
quantities, such as &field; invariants, are defined
without reference to &properties;, but there are also many
outputs, such as energy densities, that depend upon a
&property; in one way or another, and rely upon the
&property;'s code. See <xref
linkend="Section-Coding-Output"/> to learn how to add new
outputs.
</para>
<itemizedlist spacing="compact">
<listitem>
<simpara>
<link linkend="Class-Property-output"><code>Property::output</code></link>
</simpara>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</para>
</section> <!-- Adding New Material Properties -->
<section id="Section-Coding-Output">
<title>Adding New Outputs</title>
<para>
<classname>Outputs</classname> in &oof2; are operations
performed on &mesh; data. The results can be sent to the <link
linkend="Chapter-Graphics">graphics window</link> and displayed
in a <link
linkend="RegisteredClass-FilledContourDisplay">contour
plot</link>, or analyzed on the <link
linkend="Section-Tasks-Analysis">Analysis Page</link>.
</para>
<para>
The outputs that the user sees come in three flavors:
<itemizedlist spacing="compact">
<listitem>
<para>
<emphasis>Scalar</emphasis> outputs are just numbers, to
be plotted or analyzed.
</para>
</listitem>
<listitem>
<para>
<emphasis>Aggregate</emphasis> outputs are quantities like
&Fields; and &Fluxes; that can't be plotted directly
(because they generally have too many components) but can
still be analyzed.
</para>
</listitem>
<listitem>
<para>
<emphasis>Position</emphasis> outputs are two dimensional
vectors that determine where scalar outputs are displayed
in contour plots.
</para>
</listitem>
</itemizedlist>
<xref linkend="Figure-outputwidgets"/> shows how scalar and
position outputs appear in the GUI.
</para>
<figure id="Figure-outputwidgets">
<title>Output Widgets</title>
<mediaobject>
<imageobject>
<imagedata fileref="FIGURES/widgets/outputwidgets.png"
format="PNG"/>
</imageobject>
<caption>
<para>
Two output widgets, from the NOT THE LAYEREDITOR window. The
top widget, labelled <literal>what</literal>, lists scalar
outputs and determines what will be plotted in the
graphics window. The lower widget, labelled
<literal>where</literal>, lists position outputs, which
are two dimensional vectors.
</para>
</caption>
</mediaobject>
</figure>
<para>
Under the hood, the scalar, aggregate, and position outputs are
built by combining <link
linkend="Class-Output"><classname>Output</classname></link>
objects. Each <classname>Output</classname> object performs a
simple operation on a set of input values and creates a set of
output values. Simple <classname>Outputs</classname> are
chained together, with the output of one connected to the input
of another, to create more complicated
<classname>Outputs</classname>. (One
<classname>Output</classname> object can be used in many
different Output chains.) Only those
<classname>Outputs</classname> that are <link
linkend="Function-definePositionOutput">registered</link> with
the GUI are directly available to the user.
</para>
<para>
<classname>Outputs</classname> have parameters which govern
their behavior. <classname>Output</classname> parameters use
the same <link
linkend="Class-Parameter"><classname>Parameter</classname></link>
classes that are used in <link
linkend="Class-PropertyRegistration"><classname>PropertyRegistrations</classname></link>.
</para>
<para>
&oof2; includes predefined <classname>Outputs</classname> that
can be used by themselves or combined with new
<classname>Outputs</classname>. The predefined
<classname>Outputs</classname> can evaluate the components and
invariants of &Fields; and &Fluxes; and compute energies and
strains. For the details, consult the source code in
<filename>SRC/engine/IO/outputClones.py</filename> and
<filename>SRC/engine/IO/outputDefs.py</filename>.
</para>
<para>
Creating a new <classname>Output</classname> involves the
following steps:
<itemizedlist spacing="compact">
<listitem>
<para>
Write a callback function. This is what does the actual
computation. See <xref linkend="Class-Output"/> for the details.
</para>
</listitem>
<listitem>
<para>
Invoke the <classname>Output</classname> <link
linkend="Class-Output-constructor">constructor</link>,
specifying the type of the <classname>Output</classname>,
the types of its inputs (if any), its parameters (if any),
and its callback function.
</para>
</listitem>
<listitem>
<para>
Connect the <classname>Output</classname> to its inputs,
if any, by calling <link
linkend="Class-Output-connect"><methodname>Output.connect</methodname></link>.
</para>
</listitem>
<listitem>
<para>
Set parameters in the inputs, if desired.
<classname>Parameters</classname> which have fixed values
in the <classname>Output</classname> definition will
<emphasis>not</emphasis> be settable by the user. To make
a <classname>Parameter</classname> settable by the user,
leave its value alone (or set it to
<literal>None</literal>). Use <link
linkend="Class-Output-findParam"><methodname>Output.findParam</methodname></link>
or <link
linkend="Class-Output-resolveAlias"><methodname>Output.resolveAlias</methodname></link>
to gain access to an <classname>Output</classname>'s
parameters, or those of its connected inputs.
</para>
</listitem>
<listitem>
<para>
If any of the inputs have parameters that are
<emphasis>not</emphasis> specified in advance, these need
to given aliases so that they can be used in scripts and
the GUI. See <link
linkend="Class-Output-aliasParam"><methodname>Output.aliasParam</methodname></link> for the details.
</para>
</listitem>
<listitem>
<para>
If the <classname>Output </classname> is to appear in the
GUI, it must be registered by calling <link
linkend="Function-definePositionOutput"><function>definePositionOutput</function></link>,
<link
linkend="Function-definePositionOutput"><function>defineScalarOutput</function></link>,
or <link
linkend="Function-definePositionOutput"><function>defineAggregateOutput</function></link>.
</para>
</listitem>
</itemizedlist>
</para>
<section id="Section-Coding-Output-Examples">
<title>Examples</title>
<para>
Here are a few example <classname>Output</classname>
definitions, extracted from the &oof2; source code. The
examples illustrate all of the important features of the
<classname>Output</classname> class.
</para>
<section id="Section-Coding-Output-Example1">
<title>Evaluating a Field</title>
<para>
The first example returns <classname>Field</classname>
values as <link
linkend="Class-OutputVal"><classname>OutputVal</classname></link>
instances. It's copied<footnote>
<para>
Ok, we lied. The example is copied from an
<emphasis>old</emphasis> version of the source code.
The given example is very inefficient because the
<code>+=</code> inside the loop is continuously
reallocating and copying the <varname>ans</varname>
list. See the actual code in
<filename>SRC/engine/IO/outputClones.py</filename> for a
more efficient but less obvious scheme.
</para>
</footnote>