/[escript]/branches/DataC_2092/escript/src/DataLazy.cpp
ViewVC logotype

Contents of /branches/DataC_2092/escript/src/DataLazy.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2103 - (show annotations)
Thu Nov 27 01:00:34 2008 UTC (14 years, 3 months ago) by jfenwick
File size: 43502 byte(s)
Branch commit.
Added a note about mismatched allocs and decallocs.
Added Data::actsExpanded and friends.
Modified DataC::isExpanded to call it instead of Data::isExpanded.
1
2 /*******************************************************
3 *
4 * Copyright (c) 2003-2008 by University of Queensland
5 * Earth Systems Science Computational Center (ESSCC)
6 * http://www.uq.edu.au/esscc
7 *
8 * Primary Business: Queensland, Australia
9 * Licensed under the Open Software License version 3.0
10 * http://www.opensource.org/licenses/osl-3.0.php
11 *
12 *******************************************************/
13
14
15 #include "DataLazy.h"
16 #ifdef USE_NETCDF
17 #include <netcdfcpp.h>
18 #endif
19 #ifdef PASO_MPI
20 #include <mpi.h>
21 #endif
22 #ifdef _OPENMP
23 #include <omp.h>
24 #endif
25 #include "FunctionSpace.h"
26 #include "DataTypes.h"
27 #include "Data.h"
28 #include "UnaryFuncs.h" // for escript::fsign
29 #include "Utils.h"
30
31 // #define LAZYDEBUG(X) X;
32 #define LAZYDEBUG(X)
33
34 /*
35 How does DataLazy work?
36 ~~~~~~~~~~~~~~~~~~~~~~~
37
38 Each instance represents a single operation on one or two other DataLazy instances. These arguments are normally
39 denoted left and right.
40
41 A special operation, IDENTITY, stores an instance of DataReady in the m_id member.
42 This means that all "internal" nodes in the structure are instances of DataLazy.
43
44 Each operation has a string representation as well as an opgroup - eg G_IDENTITY, G_BINARY, ...
45 Note that IDENITY is not considered a unary operation.
46
47 I am avoiding calling the structure formed a tree because it is not guaranteed to be one (eg c=a+a).
48 It must however form a DAG (directed acyclic graph).
49 I will refer to individual DataLazy objects with the structure as nodes.
50
51 Each node also stores:
52 - m_readytype \in {'E','T','C','?'} ~ indicates what sort of DataReady would be produced if the expression was
53 evaluated.
54 - m_buffsrequired ~ the larged number of samples which would need to be kept simultaneously in order to
55 evaluate the expression.
56 - m_samplesize ~ the number of doubles stored in a sample.
57
58 When a new node is created, the above values are computed based on the values in the child nodes.
59 Eg: if left requires 4 samples and right requires 6 then left+right requires 7 samples.
60
61 The resolve method, which produces a DataReady from a DataLazy, does the following:
62 1) Create a DataReady to hold the new result.
63 2) Allocate a vector (v) big enough to hold m_buffsrequired samples.
64 3) For each sample, call resolveSample with v, to get its values and copy them into the result object.
65
66 (In the case of OMP, multiple samples are resolved in parallel so the vector needs to be larger.)
67
68 resolveSample returns a Vector* and an offset within that vector where the result is stored.
69 Normally, this would be v, but for identity nodes their internal vector is returned instead.
70
71 The convention that I use, is that the resolve methods should store their results starting at the offset they are passed.
72
73 For expressions which evaluate to Constant or Tagged, there is a different evaluation method.
74 The collapse method invokes the (non-lazy) operations on the Data class to evaluate the expression.
75
76 To add a new operator you need to do the following (plus anything I might have forgotten):
77 1) Add to the ES_optype.
78 2) determine what opgroup your operation belongs to (X)
79 3) add a string for the op to the end of ES_opstrings
80 4) increase ES_opcount
81 5) add an entry (X) to opgroups
82 6) add an entry to the switch in collapseToReady
83 7) add an entry to resolveX
84 */
85
86
87 using namespace std;
88 using namespace boost;
89
90 namespace escript
91 {
92
93 namespace
94 {
95
96 enum ES_opgroup
97 {
98 G_UNKNOWN,
99 G_IDENTITY,
100 G_BINARY, // pointwise operations with two arguments
101 G_UNARY, // pointwise operations with one argument
102 G_NP1OUT, // non-pointwise op with one output
103 G_NP1OUT_P, // non-pointwise op with one output requiring a parameter
104 G_TENSORPROD // general tensor product
105 };
106
107
108
109
110 string ES_opstrings[]={"UNKNOWN","IDENTITY","+","-","*","/","^",
111 "sin","cos","tan",
112 "asin","acos","atan","sinh","cosh","tanh","erf",
113 "asinh","acosh","atanh",
114 "log10","log","sign","abs","neg","pos","exp","sqrt",
115 "1/","where>0","where<0","where>=0","where<=0",
116 "symmetric","nonsymmetric",
117 "prod",
118 "transpose",
119 "trace"};
120 int ES_opcount=38;
121 ES_opgroup opgroups[]={G_UNKNOWN,G_IDENTITY,G_BINARY,G_BINARY,G_BINARY,G_BINARY, G_BINARY,
122 G_UNARY,G_UNARY,G_UNARY, //10
123 G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY, // 17
124 G_UNARY,G_UNARY,G_UNARY, // 20
125 G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY, // 28
126 G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY, // 33
127 G_NP1OUT,G_NP1OUT,
128 G_TENSORPROD,
129 G_NP1OUT_P, G_NP1OUT_P};
130 inline
131 ES_opgroup
132 getOpgroup(ES_optype op)
133 {
134 return opgroups[op];
135 }
136
137 // return the FunctionSpace of the result of "left op right"
138 FunctionSpace
139 resultFS(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op)
140 {
141 // perhaps this should call interpolate and throw or something?
142 // maybe we need an interpolate node -
143 // that way, if interpolate is required in any other op we can just throw a
144 // programming error exception.
145
146 FunctionSpace l=left->getFunctionSpace();
147 FunctionSpace r=right->getFunctionSpace();
148 if (l!=r)
149 {
150 if (r.probeInterpolation(l))
151 {
152 return l;
153 }
154 if (l.probeInterpolation(r))
155 {
156 return r;
157 }
158 throw DataException("Cannot interpolate between the FunctionSpaces given for operation "+opToString(op)+".");
159 }
160 return l;
161 }
162
163 // return the shape of the result of "left op right"
164 // the shapes resulting from tensor product are more complex to compute so are worked out elsewhere
165 DataTypes::ShapeType
166 resultShape(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op)
167 {
168 if (left->getShape()!=right->getShape())
169 {
170 if ((getOpgroup(op)!=G_BINARY) && (getOpgroup(op)!=G_NP1OUT))
171 {
172 throw DataException("Shapes not the name - shapes must match for (point)binary operations.");
173 }
174 if (left->getRank()==0) // we need to allow scalar * anything
175 {
176 return right->getShape();
177 }
178 if (right->getRank()==0)
179 {
180 return left->getShape();
181 }
182 throw DataException("Shapes not the same - arguments must have matching shapes (or be scalars) for (point)binary operations on lazy data.");
183 }
184 return left->getShape();
185 }
186
187 // return the shape for "op left"
188
189 DataTypes::ShapeType
190 resultShape(DataAbstract_ptr left, ES_optype op)
191 {
192 switch(op)
193 {
194 case TRANS:
195 return left->getShape();
196 break;
197 case TRACE:
198 return DataTypes::scalarShape;
199 break;
200 default:
201 throw DataException("Programmer error - resultShape(left,op) can't compute shapes for operator "+opToString(op)+".");
202 }
203 }
204
205 // determine the output shape for the general tensor product operation
206 // the additional parameters return information required later for the product
207 // the majority of this code is copy pasted from C_General_Tensor_Product
208 DataTypes::ShapeType
209 GTPShape(DataAbstract_ptr left, DataAbstract_ptr right, int axis_offset, int transpose, int& SL, int& SM, int& SR)
210 {
211
212 // Get rank and shape of inputs
213 int rank0 = left->getRank();
214 int rank1 = right->getRank();
215 const DataTypes::ShapeType& shape0 = left->getShape();
216 const DataTypes::ShapeType& shape1 = right->getShape();
217
218 // Prepare for the loops of the product and verify compatibility of shapes
219 int start0=0, start1=0;
220 if (transpose == 0) {}
221 else if (transpose == 1) { start0 = axis_offset; }
222 else if (transpose == 2) { start1 = rank1-axis_offset; }
223 else { throw DataException("DataLazy GeneralTensorProduct Constructor: Error - transpose should be 0, 1 or 2"); }
224
225 if (rank0<axis_offset)
226 {
227 throw DataException("DataLazy GeneralTensorProduct Constructor: Error - rank of left < axisoffset");
228 }
229
230 // Adjust the shapes for transpose
231 DataTypes::ShapeType tmpShape0(rank0); // pre-sizing the vectors rather
232 DataTypes::ShapeType tmpShape1(rank1); // than using push_back
233 for (int i=0; i<rank0; i++) { tmpShape0[i]=shape0[(i+start0)%rank0]; }
234 for (int i=0; i<rank1; i++) { tmpShape1[i]=shape1[(i+start1)%rank1]; }
235
236 // Prepare for the loops of the product
237 SL=1, SM=1, SR=1;
238 for (int i=0; i<rank0-axis_offset; i++) {
239 SL *= tmpShape0[i];
240 }
241 for (int i=rank0-axis_offset; i<rank0; i++) {
242 if (tmpShape0[i] != tmpShape1[i-(rank0-axis_offset)]) {
243 throw DataException("C_GeneralTensorProduct: Error - incompatible shapes");
244 }
245 SM *= tmpShape0[i];
246 }
247 for (int i=axis_offset; i<rank1; i++) {
248 SR *= tmpShape1[i];
249 }
250
251 // Define the shape of the output (rank of shape is the sum of the loop ranges below)
252 DataTypes::ShapeType shape2(rank0+rank1-2*axis_offset);
253 { // block to limit the scope of out_index
254 int out_index=0;
255 for (int i=0; i<rank0-axis_offset; i++, ++out_index) { shape2[out_index]=tmpShape0[i]; } // First part of arg_0_Z
256 for (int i=axis_offset; i<rank1; i++, ++out_index) { shape2[out_index]=tmpShape1[i]; } // Last part of arg_1_Z
257 }
258
259 if (shape2.size()>ESCRIPT_MAX_DATA_RANK)
260 {
261 ostringstream os;
262 os << "C_GeneralTensorProduct: Error - Attempt to create a rank " << shape2.size() << " object. The maximum rank is " << ESCRIPT_MAX_DATA_RANK << ".";
263 throw DataException(os.str());
264 }
265
266 return shape2;
267 }
268
269
270 // determine the number of points in the result of "left op right"
271 // note that determining the resultLength for G_TENSORPROD is more complex and will not be processed here
272 // size_t
273 // resultLength(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op)
274 // {
275 // switch (getOpgroup(op))
276 // {
277 // case G_BINARY: return left->getLength();
278 // case G_UNARY: return left->getLength();
279 // case G_NP1OUT: return left->getLength();
280 // default:
281 // throw DataException("Programmer Error - attempt to getLength() for operator "+opToString(op)+".");
282 // }
283 // }
284
285 // determine the number of samples requires to evaluate an expression combining left and right
286 // NP1OUT needs an extra buffer because we can't write the answers over the top of the input.
287 // The same goes for G_TENSORPROD
288 int
289 calcBuffs(const DataLazy_ptr& left, const DataLazy_ptr& right, ES_optype op)
290 {
291 switch(getOpgroup(op))
292 {
293 case G_IDENTITY: return 1;
294 case G_BINARY: return max(left->getBuffsRequired(),right->getBuffsRequired()+1);
295 case G_UNARY: return max(left->getBuffsRequired(),1);
296 case G_NP1OUT: return 1+max(left->getBuffsRequired(),1);
297 case G_NP1OUT_P: return 1+max(left->getBuffsRequired(),1);
298 case G_TENSORPROD: return 1+max(left->getBuffsRequired(),right->getBuffsRequired()+1);
299 default:
300 throw DataException("Programmer Error - attempt to calcBuffs() for operator "+opToString(op)+".");
301 }
302 }
303
304
305 } // end anonymous namespace
306
307
308
309 // Return a string representing the operation
310 const std::string&
311 opToString(ES_optype op)
312 {
313 if (op<0 || op>=ES_opcount)
314 {
315 op=UNKNOWNOP;
316 }
317 return ES_opstrings[op];
318 }
319
320
321 DataLazy::DataLazy(DataAbstract_ptr p)
322 : parent(p->getFunctionSpace(),p->getShape()),
323 m_op(IDENTITY),
324 m_axis_offset(0),
325 m_transpose(0),
326 m_SL(0), m_SM(0), m_SR(0)
327 {
328 if (p->isLazy())
329 {
330 // I don't want identity of Lazy.
331 // Question: Why would that be so bad?
332 // Answer: We assume that the child of ID is something we can call getVector on
333 throw DataException("Programmer error - attempt to create identity from a DataLazy.");
334 }
335 else
336 {
337 m_id=dynamic_pointer_cast<DataReady>(p);
338 if(p->isConstant()) {m_readytype='C';}
339 else if(p->isExpanded()) {m_readytype='E';}
340 else if (p->isTagged()) {m_readytype='T';}
341 else {throw DataException("Unknown DataReady instance in DataLazy constructor.");}
342 }
343 m_buffsRequired=1;
344 m_samplesize=getNumDPPSample()*getNoValues();
345 m_maxsamplesize=m_samplesize;
346 LAZYDEBUG(cout << "(1)Lazy created with " << m_samplesize << endl;)
347 }
348
349
350
351
352 DataLazy::DataLazy(DataAbstract_ptr left, ES_optype op)
353 : parent(left->getFunctionSpace(),left->getShape()),
354 m_op(op),
355 m_axis_offset(0),
356 m_transpose(0),
357 m_SL(0), m_SM(0), m_SR(0)
358 {
359 if ((getOpgroup(op)!=G_UNARY) && (getOpgroup(op)!=G_NP1OUT))
360 {
361 throw DataException("Programmer error - constructor DataLazy(left, op) will only process UNARY operations.");
362 }
363
364 DataLazy_ptr lleft;
365 if (!left->isLazy())
366 {
367 lleft=DataLazy_ptr(new DataLazy(left));
368 }
369 else
370 {
371 lleft=dynamic_pointer_cast<DataLazy>(left);
372 }
373 m_readytype=lleft->m_readytype;
374 m_left=lleft;
375 m_buffsRequired=calcBuffs(m_left, m_right,m_op); // yeah m_right will be null at this point
376 m_samplesize=getNumDPPSample()*getNoValues();
377 m_maxsamplesize=max(m_samplesize,m_left->getMaxSampleSize());
378 }
379
380
381 // In this constructor we need to consider interpolation
382 DataLazy::DataLazy(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op)
383 : parent(resultFS(left,right,op), resultShape(left,right,op)),
384 m_op(op),
385 m_SL(0), m_SM(0), m_SR(0)
386 {
387 if ((getOpgroup(op)!=G_BINARY))
388 {
389 throw DataException("Programmer error - constructor DataLazy(left, right, op) will only process BINARY operations.");
390 }
391
392 if (getFunctionSpace()!=left->getFunctionSpace()) // left needs to be interpolated
393 {
394 FunctionSpace fs=getFunctionSpace();
395 Data ltemp(left);
396 Data tmp(ltemp,fs);
397 left=tmp.borrowDataPtr();
398 }
399 if (getFunctionSpace()!=right->getFunctionSpace()) // right needs to be interpolated
400 {
401 Data tmp(Data(right),getFunctionSpace());
402 right=tmp.borrowDataPtr();
403 }
404 left->operandCheck(*right);
405
406 if (left->isLazy()) // the children need to be DataLazy. Wrap them in IDENTITY if required
407 {
408 m_left=dynamic_pointer_cast<DataLazy>(left);
409 }
410 else
411 {
412 m_left=DataLazy_ptr(new DataLazy(left));
413 }
414 if (right->isLazy())
415 {
416 m_right=dynamic_pointer_cast<DataLazy>(right);
417 }
418 else
419 {
420 m_right=DataLazy_ptr(new DataLazy(right));
421 }
422 char lt=m_left->m_readytype;
423 char rt=m_right->m_readytype;
424 if (lt=='E' || rt=='E')
425 {
426 m_readytype='E';
427 }
428 else if (lt=='T' || rt=='T')
429 {
430 m_readytype='T';
431 }
432 else
433 {
434 m_readytype='C';
435 }
436 m_samplesize=getNumDPPSample()*getNoValues();
437 m_maxsamplesize=max(max(m_samplesize,m_right->getMaxSampleSize()),m_left->getMaxSampleSize());
438 m_buffsRequired=calcBuffs(m_left, m_right,m_op);
439 LAZYDEBUG(cout << "(3)Lazy created with " << m_samplesize << endl;)
440 }
441
442 DataLazy::DataLazy(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op, int axis_offset, int transpose)
443 : parent(resultFS(left,right,op), GTPShape(left,right, axis_offset, transpose, m_SL,m_SM, m_SR)),
444 m_op(op),
445 m_axis_offset(axis_offset),
446 m_transpose(transpose)
447 {
448 if ((getOpgroup(op)!=G_TENSORPROD))
449 {
450 throw DataException("Programmer error - constructor DataLazy(left, right, op, ax, tr) will only process BINARY operations which require parameters.");
451 }
452 if ((transpose>2) || (transpose<0))
453 {
454 throw DataException("DataLazy GeneralTensorProduct constructor: Error - transpose should be 0, 1 or 2");
455 }
456 if (getFunctionSpace()!=left->getFunctionSpace()) // left needs to be interpolated
457 {
458 FunctionSpace fs=getFunctionSpace();
459 Data ltemp(left);
460 Data tmp(ltemp,fs);
461 left=tmp.borrowDataPtr();
462 }
463 if (getFunctionSpace()!=right->getFunctionSpace()) // right needs to be interpolated
464 {
465 Data tmp(Data(right),getFunctionSpace());
466 right=tmp.borrowDataPtr();
467 }
468 left->operandCheck(*right);
469
470 if (left->isLazy()) // the children need to be DataLazy. Wrap them in IDENTITY if required
471 {
472 m_left=dynamic_pointer_cast<DataLazy>(left);
473 }
474 else
475 {
476 m_left=DataLazy_ptr(new DataLazy(left));
477 }
478 if (right->isLazy())
479 {
480 m_right=dynamic_pointer_cast<DataLazy>(right);
481 }
482 else
483 {
484 m_right=DataLazy_ptr(new DataLazy(right));
485 }
486 char lt=m_left->m_readytype;
487 char rt=m_right->m_readytype;
488 if (lt=='E' || rt=='E')
489 {
490 m_readytype='E';
491 }
492 else if (lt=='T' || rt=='T')
493 {
494 m_readytype='T';
495 }
496 else
497 {
498 m_readytype='C';
499 }
500 m_samplesize=getNumDPPSample()*getNoValues();
501 m_maxsamplesize=max(max(m_samplesize,m_right->getMaxSampleSize()),m_left->getMaxSampleSize());
502 m_buffsRequired=calcBuffs(m_left, m_right,m_op);
503 LAZYDEBUG(cout << "(4)Lazy created with " << m_samplesize << endl;)
504 }
505
506
507 DataLazy::DataLazy(DataAbstract_ptr left, ES_optype op, int axis_offset)
508 : parent(left->getFunctionSpace(), resultShape(left,op)),
509 m_op(op),
510 m_axis_offset(axis_offset),
511 m_transpose(0)
512 {
513 if ((getOpgroup(op)!=G_NP1OUT_P))
514 {
515 throw DataException("Programmer error - constructor DataLazy(left, op, ax) will only process UNARY operations which require parameters.");
516 }
517 DataLazy_ptr lleft;
518 if (!left->isLazy())
519 {
520 lleft=DataLazy_ptr(new DataLazy(left));
521 }
522 else
523 {
524 lleft=dynamic_pointer_cast<DataLazy>(left);
525 }
526 m_readytype=lleft->m_readytype;
527 m_left=lleft;
528 m_buffsRequired=calcBuffs(m_left, m_right,m_op); // yeah m_right will be null at this point
529 m_samplesize=getNumDPPSample()*getNoValues();
530 m_maxsamplesize=max(m_samplesize,m_left->getMaxSampleSize());
531 LAZYDEBUG(cout << "(5)Lazy created with " << m_samplesize << endl;)
532 }
533
534
535 DataLazy::~DataLazy()
536 {
537 }
538
539
540 int
541 DataLazy::getBuffsRequired() const
542 {
543 return m_buffsRequired;
544 }
545
546
547 size_t
548 DataLazy::getMaxSampleSize() const
549 {
550 return m_maxsamplesize;
551 }
552
553
554
555 size_t
556 DataLazy::getSampleBufferSize() const
557 {
558 return m_maxsamplesize*(max(1,m_buffsRequired));
559 }
560
561 /*
562 \brief Evaluates the expression using methods on Data.
563 This does the work for the collapse method.
564 For reasons of efficiency do not call this method on DataExpanded nodes.
565 */
566 DataReady_ptr
567 DataLazy::collapseToReady()
568 {
569 if (m_readytype=='E')
570 { // this is more an efficiency concern than anything else
571 throw DataException("Programmer Error - do not use collapse on Expanded data.");
572 }
573 if (m_op==IDENTITY)
574 {
575 return m_id;
576 }
577 DataReady_ptr pleft=m_left->collapseToReady();
578 Data left(pleft);
579 Data right;
580 if ((getOpgroup(m_op)==G_BINARY) || (getOpgroup(m_op)==G_TENSORPROD))
581 {
582 right=Data(m_right->collapseToReady());
583 }
584 Data result;
585 switch(m_op)
586 {
587 case ADD:
588 result=left+right;
589 break;
590 case SUB:
591 result=left-right;
592 break;
593 case MUL:
594 result=left*right;
595 break;
596 case DIV:
597 result=left/right;
598 break;
599 case SIN:
600 result=left.sin();
601 break;
602 case COS:
603 result=left.cos();
604 break;
605 case TAN:
606 result=left.tan();
607 break;
608 case ASIN:
609 result=left.asin();
610 break;
611 case ACOS:
612 result=left.acos();
613 break;
614 case ATAN:
615 result=left.atan();
616 break;
617 case SINH:
618 result=left.sinh();
619 break;
620 case COSH:
621 result=left.cosh();
622 break;
623 case TANH:
624 result=left.tanh();
625 break;
626 case ERF:
627 result=left.erf();
628 break;
629 case ASINH:
630 result=left.asinh();
631 break;
632 case ACOSH:
633 result=left.acosh();
634 break;
635 case ATANH:
636 result=left.atanh();
637 break;
638 case LOG10:
639 result=left.log10();
640 break;
641 case LOG:
642 result=left.log();
643 break;
644 case SIGN:
645 result=left.sign();
646 break;
647 case ABS:
648 result=left.abs();
649 break;
650 case NEG:
651 result=left.neg();
652 break;
653 case POS:
654 // it doesn't mean anything for delayed.
655 // it will just trigger a deep copy of the lazy object
656 throw DataException("Programmer error - POS not supported for lazy data.");
657 break;
658 case EXP:
659 result=left.exp();
660 break;
661 case SQRT:
662 result=left.sqrt();
663 break;
664 case RECIP:
665 result=left.oneOver();
666 break;
667 case GZ:
668 result=left.wherePositive();
669 break;
670 case LZ:
671 result=left.whereNegative();
672 break;
673 case GEZ:
674 result=left.whereNonNegative();
675 break;
676 case LEZ:
677 result=left.whereNonPositive();
678 break;
679 case SYM:
680 result=left.symmetric();
681 break;
682 case NSYM:
683 result=left.nonsymmetric();
684 break;
685 case PROD:
686 result=C_GeneralTensorProduct(left,right,m_axis_offset, m_transpose);
687 break;
688 case TRANS:
689 result=left.transpose(m_axis_offset);
690 break;
691 case TRACE:
692 result=left.trace(m_axis_offset);
693 break;
694 default:
695 throw DataException("Programmer error - collapseToReady does not know how to resolve operator "+opToString(m_op)+".");
696 }
697 return result.borrowReadyPtr();
698 }
699
700 /*
701 \brief Converts the DataLazy into an IDENTITY storing the value of the expression.
702 This method uses the original methods on the Data class to evaluate the expressions.
703 For this reason, it should not be used on DataExpanded instances. (To do so would defeat
704 the purpose of using DataLazy in the first place).
705 */
706 void
707 DataLazy::collapse()
708 {
709 if (m_op==IDENTITY)
710 {
711 return;
712 }
713 if (m_readytype=='E')
714 { // this is more an efficiency concern than anything else
715 throw DataException("Programmer Error - do not use collapse on Expanded data.");
716 }
717 m_id=collapseToReady();
718 m_op=IDENTITY;
719 }
720
721 /*
722 \brief Compute the value of the expression (unary operation) for the given sample.
723 \return Vector which stores the value of the subexpression for the given sample.
724 \param v A vector to store intermediate results.
725 \param offset Index in v to begin storing results.
726 \param sampleNo Sample number to evaluate.
727 \param roffset (output parameter) the offset in the return vector where the result begins.
728
729 The return value will be an existing vector so do not deallocate it.
730 If the result is stored in v it should be stored at the offset given.
731 Everything from offset to the end of v should be considered available for this method to use.
732 */
733 DataTypes::ValueType*
734 DataLazy::resolveUnary(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const
735 {
736 // we assume that any collapsing has been done before we get here
737 // since we only have one argument we don't need to think about only
738 // processing single points.
739 if (m_readytype!='E')
740 {
741 throw DataException("Programmer error - resolveUnary should only be called on expanded Data.");
742 }
743 const ValueType* vleft=m_left->resolveSample(v,offset,sampleNo,roffset);
744 const double* left=&((*vleft)[roffset]);
745 double* result=&(v[offset]);
746 roffset=offset;
747 switch (m_op)
748 {
749 case SIN:
750 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::sin);
751 break;
752 case COS:
753 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::cos);
754 break;
755 case TAN:
756 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::tan);
757 break;
758 case ASIN:
759 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::asin);
760 break;
761 case ACOS:
762 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::acos);
763 break;
764 case ATAN:
765 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::atan);
766 break;
767 case SINH:
768 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::sinh);
769 break;
770 case COSH:
771 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::cosh);
772 break;
773 case TANH:
774 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::tanh);
775 break;
776 case ERF:
777 #if defined (_WIN32) && !defined(__INTEL_COMPILER)
778 throw DataException("Error - Data:: erf function is not supported on _WIN32 platforms.");
779 #else
780 tensor_unary_operation(m_samplesize, left, result, ::erf);
781 break;
782 #endif
783 case ASINH:
784 #if defined (_WIN32) && !defined(__INTEL_COMPILER)
785 tensor_unary_operation(m_samplesize, left, result, escript::asinh_substitute);
786 #else
787 tensor_unary_operation(m_samplesize, left, result, ::asinh);
788 #endif
789 break;
790 case ACOSH:
791 #if defined (_WIN32) && !defined(__INTEL_COMPILER)
792 tensor_unary_operation(m_samplesize, left, result, escript::acosh_substitute);
793 #else
794 tensor_unary_operation(m_samplesize, left, result, ::acosh);
795 #endif
796 break;
797 case ATANH:
798 #if defined (_WIN32) && !defined(__INTEL_COMPILER)
799 tensor_unary_operation(m_samplesize, left, result, escript::atanh_substitute);
800 #else
801 tensor_unary_operation(m_samplesize, left, result, ::atanh);
802 #endif
803 break;
804 case LOG10:
805 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::log10);
806 break;
807 case LOG:
808 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::log);
809 break;
810 case SIGN:
811 tensor_unary_operation(m_samplesize, left, result, escript::fsign);
812 break;
813 case ABS:
814 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::fabs);
815 break;
816 case NEG:
817 tensor_unary_operation(m_samplesize, left, result, negate<double>());
818 break;
819 case POS:
820 // it doesn't mean anything for delayed.
821 // it will just trigger a deep copy of the lazy object
822 throw DataException("Programmer error - POS not supported for lazy data.");
823 break;
824 case EXP:
825 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::exp);
826 break;
827 case SQRT:
828 tensor_unary_operation<double (*)(double)>(m_samplesize, left, result, ::sqrt);
829 break;
830 case RECIP:
831 tensor_unary_operation(m_samplesize, left, result, bind1st(divides<double>(),1.));
832 break;
833 case GZ:
834 tensor_unary_operation(m_samplesize, left, result, bind2nd(greater<double>(),0.0));
835 break;
836 case LZ:
837 tensor_unary_operation(m_samplesize, left, result, bind2nd(less<double>(),0.0));
838 break;
839 case GEZ:
840 tensor_unary_operation(m_samplesize, left, result, bind2nd(greater_equal<double>(),0.0));
841 break;
842 case LEZ:
843 tensor_unary_operation(m_samplesize, left, result, bind2nd(less_equal<double>(),0.0));
844 break;
845
846 default:
847 throw DataException("Programmer error - resolveUnary can not resolve operator "+opToString(m_op)+".");
848 }
849 return &v;
850 }
851
852
853 /*
854 \brief Compute the value of the expression (unary operation) for the given sample.
855 \return Vector which stores the value of the subexpression for the given sample.
856 \param v A vector to store intermediate results.
857 \param offset Index in v to begin storing results.
858 \param sampleNo Sample number to evaluate.
859 \param roffset (output parameter) the offset in the return vector where the result begins.
860
861 The return value will be an existing vector so do not deallocate it.
862 If the result is stored in v it should be stored at the offset given.
863 Everything from offset to the end of v should be considered available for this method to use.
864 */
865 DataTypes::ValueType*
866 DataLazy::resolveNP1OUT(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const
867 {
868 // we assume that any collapsing has been done before we get here
869 // since we only have one argument we don't need to think about only
870 // processing single points.
871 if (m_readytype!='E')
872 {
873 throw DataException("Programmer error - resolveNP1OUT should only be called on expanded Data.");
874 }
875 // since we can't write the result over the input, we need a result offset further along
876 size_t subroffset=roffset+m_samplesize;
877 const ValueType* vleft=m_left->resolveSample(v,offset,sampleNo,subroffset);
878 roffset=offset;
879 switch (m_op)
880 {
881 case SYM:
882 DataMaths::symmetric(*vleft,m_left->getShape(),subroffset, v, getShape(), offset);
883 break;
884 case NSYM:
885 DataMaths::nonsymmetric(*vleft,m_left->getShape(),subroffset, v, getShape(), offset);
886 break;
887 default:
888 throw DataException("Programmer error - resolveNP1OUT can not resolve operator "+opToString(m_op)+".");
889 }
890 return &v;
891 }
892
893 /*
894 \brief Compute the value of the expression (unary operation) for the given sample.
895 \return Vector which stores the value of the subexpression for the given sample.
896 \param v A vector to store intermediate results.
897 \param offset Index in v to begin storing results.
898 \param sampleNo Sample number to evaluate.
899 \param roffset (output parameter) the offset in the return vector where the result begins.
900
901 The return value will be an existing vector so do not deallocate it.
902 If the result is stored in v it should be stored at the offset given.
903 Everything from offset to the end of v should be considered available for this method to use.
904 */
905 DataTypes::ValueType*
906 DataLazy::resolveNP1OUT_P(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const
907 {
908 // we assume that any collapsing has been done before we get here
909 // since we only have one argument we don't need to think about only
910 // processing single points.
911 if (m_readytype!='E')
912 {
913 throw DataException("Programmer error - resolveNP1OUT_P should only be called on expanded Data.");
914 }
915 // since we can't write the result over the input, we need a result offset further along
916 size_t subroffset=roffset+m_samplesize;
917 const ValueType* vleft=m_left->resolveSample(v,offset,sampleNo,subroffset);
918 roffset=offset;
919 switch (m_op)
920 {
921 case TRACE:
922 DataMaths::trace(*vleft,m_left->getShape(),subroffset, v,getShape(),offset,m_axis_offset);
923 break;
924 case TRANS:
925 DataMaths::transpose(*vleft,m_left->getShape(),subroffset, v,getShape(),offset,m_axis_offset);
926 break;
927 default:
928 throw DataException("Programmer error - resolveNP1OUTP can not resolve operator "+opToString(m_op)+".");
929 }
930 return &v;
931 }
932
933
934 #define PROC_OP(TYPE,X) \
935 for (int i=0;i<steps;++i,resultp+=resultStep) \
936 { \
937 tensor_binary_operation< TYPE >(chunksize, &((*left)[lroffset]), &((*right)[rroffset]), resultp, X); \
938 lroffset+=leftStep; \
939 rroffset+=rightStep; \
940 }
941
942 /*
943 \brief Compute the value of the expression (binary operation) for the given sample.
944 \return Vector which stores the value of the subexpression for the given sample.
945 \param v A vector to store intermediate results.
946 \param offset Index in v to begin storing results.
947 \param sampleNo Sample number to evaluate.
948 \param roffset (output parameter) the offset in the return vector where the result begins.
949
950 The return value will be an existing vector so do not deallocate it.
951 If the result is stored in v it should be stored at the offset given.
952 Everything from offset to the end of v should be considered available for this method to use.
953 */
954 // This method assumes that any subexpressions which evaluate to Constant or Tagged Data
955 // have already been collapsed to IDENTITY. So we must have at least one expanded child.
956 // If both children are expanded, then we can process them in a single operation (we treat
957 // the whole sample as one big datapoint.
958 // If one of the children is not expanded, then we need to treat each point in the sample
959 // individually.
960 // There is an additional complication when scalar operations are considered.
961 // For example, 2+Vector.
962 // In this case each double within the point is treated individually
963 DataTypes::ValueType*
964 DataLazy::resolveBinary(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const
965 {
966 LAZYDEBUG(cout << "Resolve binary: " << toString() << endl;)
967
968 size_t lroffset=0, rroffset=0; // offsets in the left and right result vectors
969 // first work out which of the children are expanded
970 bool leftExp=(m_left->m_readytype=='E');
971 bool rightExp=(m_right->m_readytype=='E');
972 if (!leftExp && !rightExp)
973 {
974 throw DataException("Programmer Error - please use collapse if neither argument has type 'E'.");
975 }
976 bool leftScalar=(m_left->getRank()==0);
977 bool rightScalar=(m_right->getRank()==0);
978 bool bigloops=((leftExp && rightExp) || (!leftExp && !rightExp)); // is processing in single step?
979 int steps=(bigloops?1:getNumDPPSample());
980 size_t chunksize=(bigloops? m_samplesize : getNoValues()); // if bigloops, pretend the whole sample is a datapoint
981 if (m_left->getRank()!=m_right->getRank()) // need to deal with scalar * ? ops
982 {
983 if (!leftScalar && !rightScalar)
984 {
985 throw DataException("resolveBinary - ranks of arguments must match unless one of them is scalar.");
986 }
987 steps=getNumDPPSample()*max(m_left->getNoValues(),m_right->getNoValues());
988 chunksize=1; // for scalar
989 }
990 int leftStep=((leftExp && (!rightExp || rightScalar))? m_right->getNoValues() : 0);
991 int rightStep=((rightExp && (!leftExp || leftScalar))? m_left->getNoValues() : 0);
992 int resultStep=max(leftStep,rightStep); // only one (at most) should be !=0
993 // Get the values of sub-expressions
994 const ValueType* left=m_left->resolveSample(v,offset,sampleNo,lroffset);
995 const ValueType* right=m_right->resolveSample(v,offset+m_samplesize,sampleNo,rroffset); // Note
996 // the right child starts further along.
997 double* resultp=&(v[offset]); // results are stored at the vector offset we recieved
998 switch(m_op)
999 {
1000 case ADD:
1001 PROC_OP(NO_ARG,plus<double>());
1002 break;
1003 case SUB:
1004 PROC_OP(NO_ARG,minus<double>());
1005 break;
1006 case MUL:
1007 PROC_OP(NO_ARG,multiplies<double>());
1008 break;
1009 case DIV:
1010 PROC_OP(NO_ARG,divides<double>());
1011 break;
1012 case POW:
1013 PROC_OP(double (double,double),::pow);
1014 break;
1015 default:
1016 throw DataException("Programmer error - resolveBinary can not resolve operator "+opToString(m_op)+".");
1017 }
1018 roffset=offset;
1019 return &v;
1020 }
1021
1022
1023 /*
1024 \brief Compute the value of the expression (tensor product) for the given sample.
1025 \return Vector which stores the value of the subexpression for the given sample.
1026 \param v A vector to store intermediate results.
1027 \param offset Index in v to begin storing results.
1028 \param sampleNo Sample number to evaluate.
1029 \param roffset (output parameter) the offset in the return vector where the result begins.
1030
1031 The return value will be an existing vector so do not deallocate it.
1032 If the result is stored in v it should be stored at the offset given.
1033 Everything from offset to the end of v should be considered available for this method to use.
1034 */
1035 // This method assumes that any subexpressions which evaluate to Constant or Tagged Data
1036 // have already been collapsed to IDENTITY. So we must have at least one expanded child.
1037 // unlike the other resolve helpers, we must treat these datapoints separately.
1038 DataTypes::ValueType*
1039 DataLazy::resolveTProd(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const
1040 {
1041 LAZYDEBUG(cout << "Resolve TensorProduct: " << toString() << endl;)
1042
1043 size_t lroffset=0, rroffset=0; // offsets in the left and right result vectors
1044 // first work out which of the children are expanded
1045 bool leftExp=(m_left->m_readytype=='E');
1046 bool rightExp=(m_right->m_readytype=='E');
1047 int steps=getNumDPPSample();
1048 int leftStep=((leftExp && !rightExp)? m_right->getNoValues() : 0);
1049 int rightStep=((rightExp && !leftExp)? m_left->getNoValues() : 0);
1050 int resultStep=max(leftStep,rightStep); // only one (at most) should be !=0
1051 // Get the values of sub-expressions (leave a gap of one sample for the result).
1052 const ValueType* left=m_left->resolveSample(v,offset+m_samplesize,sampleNo,lroffset);
1053 const ValueType* right=m_right->resolveSample(v,offset+2*m_samplesize,sampleNo,rroffset);
1054 double* resultp=&(v[offset]); // results are stored at the vector offset we recieved
1055 switch(m_op)
1056 {
1057 case PROD:
1058 for (int i=0;i<steps;++i,resultp+=resultStep)
1059 {
1060 const double *ptr_0 = &((*left)[lroffset]);
1061 const double *ptr_1 = &((*right)[rroffset]);
1062 matrix_matrix_product(m_SL, m_SM, m_SR, ptr_0, ptr_1, resultp, m_transpose);
1063 lroffset+=leftStep;
1064 rroffset+=rightStep;
1065 }
1066 break;
1067 default:
1068 throw DataException("Programmer error - resolveTProduct can not resolve operator "+opToString(m_op)+".");
1069 }
1070 roffset=offset;
1071 return &v;
1072 }
1073
1074
1075
1076 /*
1077 \brief Compute the value of the expression for the given sample.
1078 \return Vector which stores the value of the subexpression for the given sample.
1079 \param v A vector to store intermediate results.
1080 \param offset Index in v to begin storing results.
1081 \param sampleNo Sample number to evaluate.
1082 \param roffset (output parameter) the offset in the return vector where the result begins.
1083
1084 The return value will be an existing vector so do not deallocate it.
1085 */
1086 // the vector and the offset are a place where the method could write its data if it wishes
1087 // it is not obligated to do so. For example, if it has its own storage already, it can use that.
1088 // Hence the return value to indicate where the data is actually stored.
1089 // Regardless, the storage should be assumed to be used, even if it isn't.
1090
1091 // the roffset is the offset within the returned vector where the data begins
1092 const DataTypes::ValueType*
1093 DataLazy::resolveSample(ValueType& v, size_t offset, int sampleNo, size_t& roffset)
1094 {
1095 LAZYDEBUG(cout << "Resolve sample " << toString() << endl;)
1096 // collapse so we have a 'E' node or an IDENTITY for some other type
1097 if (m_readytype!='E' && m_op!=IDENTITY)
1098 {
1099 collapse();
1100 }
1101 if (m_op==IDENTITY)
1102 {
1103 const ValueType& vec=m_id->getVector();
1104 if (m_readytype=='C')
1105 {
1106 roffset=0;
1107 return &(vec);
1108 }
1109 roffset=m_id->getPointOffset(sampleNo, 0);
1110 return &(vec);
1111 }
1112 if (m_readytype!='E')
1113 {
1114 throw DataException("Programmer Error - Collapse did not produce an expanded node.");
1115 }
1116 switch (getOpgroup(m_op))
1117 {
1118 case G_UNARY: return resolveUnary(v, offset,sampleNo,roffset);
1119 case G_BINARY: return resolveBinary(v, offset,sampleNo,roffset);
1120 case G_NP1OUT: return resolveNP1OUT(v, offset, sampleNo,roffset);
1121 case G_NP1OUT_P: return resolveNP1OUT_P(v, offset, sampleNo,roffset);
1122 case G_TENSORPROD: return resolveTProd(v,offset, sampleNo,roffset);
1123 default:
1124 throw DataException("Programmer Error - resolveSample does not know how to process "+opToString(m_op)+".");
1125 }
1126 }
1127
1128
1129 // To simplify the memory management, all threads operate on one large vector, rather than one each.
1130 // Each sample is evaluated independently and copied into the result DataExpanded.
1131 DataReady_ptr
1132 DataLazy::resolve()
1133 {
1134
1135 LAZYDEBUG(cout << "Sample size=" << m_samplesize << endl;)
1136 LAZYDEBUG(cout << "Buffers=" << m_buffsRequired << endl;)
1137 if (m_readytype!='E') // if the whole sub-expression is Constant or Tagged, then evaluate it normally
1138 {
1139 collapse();
1140 }
1141 if (m_op==IDENTITY) // So a lazy expression of Constant or Tagged data will be returned here.
1142 {
1143 return m_id;
1144 }
1145 // from this point on we must have m_op!=IDENTITY and m_readytype=='E'
1146 size_t threadbuffersize=m_maxsamplesize*(max(1,m_buffsRequired)); // Each thread needs to have enough
1147 // storage to evaluate its expression
1148 int numthreads=1;
1149 #ifdef _OPENMP
1150 numthreads=getNumberOfThreads();
1151 #endif
1152 ValueType v(numthreads*threadbuffersize);
1153 LAZYDEBUG(cout << "Buffer created with size=" << v.size() << endl;)
1154 DataExpanded* result=new DataExpanded(getFunctionSpace(),getShape(), ValueType(getNoValues()));
1155 ValueType& resvec=result->getVector();
1156 DataReady_ptr resptr=DataReady_ptr(result);
1157 int sample;
1158 size_t outoffset; // offset in the output data
1159 int totalsamples=getNumSamples();
1160 const ValueType* res=0; // Vector storing the answer
1161 size_t resoffset=0; // where in the vector to find the answer
1162 #pragma omp parallel for private(sample,resoffset,outoffset,res) schedule(static)
1163 for (sample=0;sample<totalsamples;++sample)
1164 {
1165 LAZYDEBUG(cout << "################################# " << sample << endl;)
1166 #ifdef _OPENMP
1167 res=resolveSample(v,threadbuffersize*omp_get_thread_num(),sample,resoffset);
1168 #else
1169 res=resolveSample(v,0,sample,resoffset); // res would normally be v, but not if its a single IDENTITY op.
1170 #endif
1171 LAZYDEBUG(cerr << "-------------------------------- " << endl;)
1172 outoffset=result->getPointOffset(sample,0);
1173 LAZYDEBUG(cerr << "offset=" << outoffset << endl;)
1174 for (unsigned int i=0;i<m_samplesize;++i,++outoffset,++resoffset) // copy values into the output vector
1175 {
1176 resvec[outoffset]=(*res)[resoffset];
1177 }
1178 LAZYDEBUG(cerr << "*********************************" << endl;)
1179 }
1180 return resptr;
1181 }
1182
1183 std::string
1184 DataLazy::toString() const
1185 {
1186 ostringstream oss;
1187 oss << "Lazy Data:";
1188 intoString(oss);
1189 return oss.str();
1190 }
1191
1192
1193 void
1194 DataLazy::intoString(ostringstream& oss) const
1195 {
1196 switch (getOpgroup(m_op))
1197 {
1198 case G_IDENTITY:
1199 if (m_id->isExpanded())
1200 {
1201 oss << "E";
1202 }
1203 else if (m_id->isTagged())
1204 {
1205 oss << "T";
1206 }
1207 else if (m_id->isConstant())
1208 {
1209 oss << "C";
1210 }
1211 else
1212 {
1213 oss << "?";
1214 }
1215 oss << '@' << m_id.get();
1216 break;
1217 case G_BINARY:
1218 oss << '(';
1219 m_left->intoString(oss);
1220 oss << ' ' << opToString(m_op) << ' ';
1221 m_right->intoString(oss);
1222 oss << ')';
1223 break;
1224 case G_UNARY:
1225 case G_NP1OUT:
1226 case G_NP1OUT_P:
1227 oss << opToString(m_op) << '(';
1228 m_left->intoString(oss);
1229 oss << ')';
1230 break;
1231 case G_TENSORPROD:
1232 oss << opToString(m_op) << '(';
1233 m_left->intoString(oss);
1234 oss << ", ";
1235 m_right->intoString(oss);
1236 oss << ')';
1237 break;
1238 default:
1239 oss << "UNKNOWN";
1240 }
1241 }
1242
1243 DataAbstract*
1244 DataLazy::deepCopy()
1245 {
1246 switch (getOpgroup(m_op))
1247 {
1248 case G_IDENTITY: return new DataLazy(m_id->deepCopy()->getPtr());
1249 case G_UNARY: return new DataLazy(m_left->deepCopy()->getPtr(),m_op);
1250 case G_BINARY: return new DataLazy(m_left->deepCopy()->getPtr(),m_right->deepCopy()->getPtr(),m_op);
1251 case G_NP1OUT: return new DataLazy(m_left->deepCopy()->getPtr(), m_right->deepCopy()->getPtr(),m_op);
1252 case G_TENSORPROD: return new DataLazy(m_left->deepCopy()->getPtr(), m_right->deepCopy()->getPtr(), m_op, m_axis_offset, m_transpose);
1253 default:
1254 throw DataException("Programmer error - do not know how to deepcopy operator "+opToString(m_op)+".");
1255 }
1256 }
1257
1258
1259 // There is no single, natural interpretation of getLength on DataLazy.
1260 // Instances of DataReady can look at the size of their vectors.
1261 // For lazy though, it could be the size the data would be if it were resolved;
1262 // or it could be some function of the lengths of the DataReady instances which
1263 // form part of the expression.
1264 // Rather than have people making assumptions, I have disabled the method.
1265 DataTypes::ValueType::size_type
1266 DataLazy::getLength() const
1267 {
1268 throw DataException("getLength() does not make sense for lazy data.");
1269 }
1270
1271
1272 DataAbstract*
1273 DataLazy::getSlice(const DataTypes::RegionType& region) const
1274 {
1275 throw DataException("getSlice - not implemented for Lazy objects.");
1276 }
1277
1278
1279 // To do this we need to rely on our child nodes
1280 DataTypes::ValueType::size_type
1281 DataLazy::getPointOffset(int sampleNo,
1282 int dataPointNo)
1283 {
1284 if (m_op==IDENTITY)
1285 {
1286 return m_id->getPointOffset(sampleNo,dataPointNo);
1287 }
1288 if (m_readytype!='E')
1289 {
1290 collapse();
1291 return m_id->getPointOffset(sampleNo,dataPointNo);
1292 }
1293 // at this point we do not have an identity node and the expression will be Expanded
1294 // so we only need to know which child to ask
1295 if (m_left->m_readytype=='E')
1296 {
1297 return m_left->getPointOffset(sampleNo,dataPointNo);
1298 }
1299 else
1300 {
1301 return m_right->getPointOffset(sampleNo,dataPointNo);
1302 }
1303 }
1304
1305 // To do this we need to rely on our child nodes
1306 DataTypes::ValueType::size_type
1307 DataLazy::getPointOffset(int sampleNo,
1308 int dataPointNo) const
1309 {
1310 if (m_op==IDENTITY)
1311 {
1312 return m_id->getPointOffset(sampleNo,dataPointNo);
1313 }
1314 if (m_readytype=='E')
1315 {
1316 // at this point we do not have an identity node and the expression will be Expanded
1317 // so we only need to know which child to ask
1318 if (m_left->m_readytype=='E')
1319 {
1320 return m_left->getPointOffset(sampleNo,dataPointNo);
1321 }
1322 else
1323 {
1324 return m_right->getPointOffset(sampleNo,dataPointNo);
1325 }
1326 }
1327 if (m_readytype=='C')
1328 {
1329 return m_left->getPointOffset(sampleNo,dataPointNo); // which child doesn't matter
1330 }
1331 throw DataException("Programmer error - getPointOffset on lazy data may require collapsing (but this object is marked const).");
1332 }
1333
1334 // It would seem that DataTagged will need to be treated differently since even after setting all tags
1335 // to zero, all the tags from all the DataTags would be in the result.
1336 // However since they all have the same value (0) whether they are there or not should not matter.
1337 // So I have decided that for all types this method will create a constant 0.
1338 // It can be promoted up as required.
1339 // A possible efficiency concern might be expanded->constant->expanded which has an extra memory management
1340 // but we can deal with that if it arrises.
1341 void
1342 DataLazy::setToZero()
1343 {
1344 DataTypes::ValueType v(getNoValues(),0);
1345 m_id=DataReady_ptr(new DataConstant(getFunctionSpace(),getShape(),v));
1346 m_op=IDENTITY;
1347 m_right.reset();
1348 m_left.reset();
1349 m_readytype='C';
1350 m_buffsRequired=1;
1351 }
1352
1353 bool
1354 DataLazy::actsExpanded() const
1355 {
1356 return (m_readytype=='E');
1357 }
1358
1359 } // end namespace

  ViewVC Help
Powered by ViewVC 1.1.26