/[escript]/branches/schroedinger/escript/src/Data.h
ViewVC logotype

Diff of /branches/schroedinger/escript/src/Data.h

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 971 by ksteube, Wed Feb 14 04:40:49 2007 UTC revision 1802 by jfenwick, Tue Sep 23 01:03:29 2008 UTC
# Line 1  Line 1 
1  // $Id$  
2  /*  /* $Id$ */
3   ************************************************************  
4   *          Copyright 2006 by ACcESS MNRF                   *  /*******************************************************
5   *                                                          *   *
6   *              http://www.access.edu.au                    *   *           Copyright 2003-2007 by ACceSS MNRF
7   *       Primary Business: Queensland, Australia            *   *       Copyright 2007 by University of Queensland
8   *  Licensed under the Open Software License version 3.0    *   *
9   *     http://www.opensource.org/licenses/osl-3.0.php       *   *                http://esscc.uq.edu.au
10   *                                                          *   *        Primary Business: Queensland, Australia
11   ************************************************************   *  Licensed under the Open Software License version 3.0
12  */   *     http://www.opensource.org/licenses/osl-3.0.php
13     *
14     *******************************************************/
15    
16  /** \file Data.h */  /** \file Data.h */
17    
# Line 23  Line 25 
25  #include "BinaryOp.h"  #include "BinaryOp.h"
26  #include "UnaryOp.h"  #include "UnaryOp.h"
27  #include "DataException.h"  #include "DataException.h"
28    #include "DataTypes.h"
29    
30  extern "C" {  extern "C" {
31  #include "DataC.h"  #include "DataC.h"
32  #include "paso/Paso.h"  /* #include "paso/Paso.h" doesn't belong in this file...causes trouble for BruceFactory.cpp */
33  }  }
34    
35  #ifndef PASO_MPI  #include "esysmpi.h"
 #define MPI_Comm long  
 #endif  
   
36  #include <string>  #include <string>
37  #include <algorithm>  #include <algorithm>
38    #include <sstream>
39    
40    
41  #include <boost/shared_ptr.hpp>  #include <boost/shared_ptr.hpp>
42  #include <boost/python/object.hpp>  #include <boost/python/object.hpp>
# Line 51  class DataExpanded; Line 53  class DataExpanded;
53    
54  /**  /**
55     \brief     \brief
56     Data creates the appropriate Data object for the given construction     Data represents a collection of datapoints.
    arguments.  
57    
58     Description:     Description:
59     Data is essentially a factory class which creates the appropriate Data     Internally, the datapoints are actually stored by a DataAbstract object.
60     object for the given construction arguments. It retains control over     The specific instance of DataAbstract used may vary over the lifetime
61     the object created for the lifetime of the object.     of the Data object.
62     The type of Data object referred to may change during the lifetime of     Some methods on this class return references (eg getShape()).
63     the Data object.     These references should not be used after an operation which changes the underlying DataAbstract object.
64       Doing so will lead to invalid memory access.
65       This should not affect any methods exposed via boost::python.
66  */  */
67  class Data {  class Data {
68    
# Line 102  class Data { Line 105  class Data {
105         const FunctionSpace& what);         const FunctionSpace& what);
106    
107    /**    /**
108       \brief      \brief Copy Data from an existing vector
109       Constructor which copies data from a DataArrayView.    */
110    
      \param value - Input - Data value for a single point.  
      \param what - Input - A description of what this data represents.  
      \param expanded - Input - Flag, if true fill the entire container with  
                        the value. Otherwise a more efficient storage  
                        mechanism will be used.  
   */  
111    ESCRIPT_DLL_API    ESCRIPT_DLL_API
112    Data(const DataArrayView& value,    Data(const DataTypes::ValueType& value,
113         const FunctionSpace& what=FunctionSpace(),           const DataTypes::ShapeType& shape,
114         bool expanded=false);                   const FunctionSpace& what=FunctionSpace(),
115                     bool expanded=false);
116    
117    /**    /**
118       \brief       \brief
# Line 129  class Data { Line 127  class Data {
127    */    */
128    ESCRIPT_DLL_API    ESCRIPT_DLL_API
129    Data(double value,    Data(double value,
130         const DataArrayView::ShapeType& dataPointShape=DataArrayView::ShapeType(),         const DataTypes::ShapeType& dataPointShape=DataTypes::ShapeType(),
131         const FunctionSpace& what=FunctionSpace(),         const FunctionSpace& what=FunctionSpace(),
132         bool expanded=false);         bool expanded=false);
133    
# Line 142  class Data { Line 140  class Data {
140    */    */
141    ESCRIPT_DLL_API    ESCRIPT_DLL_API
142    Data(const Data& inData,    Data(const Data& inData,
143         const DataArrayView::RegionType& region);         const DataTypes::RegionType& region);
   
   /**  
      \brief  
      Constructor which will create Tagged data if expanded is false.  
      No attempt is made to ensure the tag keys match the tag keys  
      within the function space.  
   
      \param tagKeys - Input - List of tag values.  
      \param values - Input - List of values, one for each tag.  
      \param defaultValue - Input - A default value, used if tag doesn't exist.  
      \param what - Input - A description of what this data represents.  
      \param expanded - Input - Flag, if true fill the entire container with  
                        the appropriate values.  
     ==>*  
   */  
   ESCRIPT_DLL_API  
   Data(const DataTagged::TagListType& tagKeys,  
        const DataTagged::ValueListType& values,  
        const DataArrayView& defaultValue,  
        const FunctionSpace& what=FunctionSpace(),  
        bool expanded=false);  
144    
145    /**    /**
146       \brief       \brief
# Line 214  class Data { Line 191  class Data {
191       Constructor which creates a DataConstant of "shape" with constant value.       Constructor which creates a DataConstant of "shape" with constant value.
192    */    */
193    ESCRIPT_DLL_API    ESCRIPT_DLL_API
194    Data(double value,    Data(double value,
195         const boost::python::tuple& shape=boost::python::make_tuple(),         const boost::python::tuple& shape=boost::python::make_tuple(),
196         const FunctionSpace& what=FunctionSpace(),         const FunctionSpace& what=FunctionSpace(),
197         bool expanded=false);         bool expanded=false);
198    
199    
200    
201      /**
202        \brief Create a Data using an existing DataAbstract. Warning: The new object assumes ownership of the pointer!
203        Once you have passed the pointer, do not delete it.
204      */
205      ESCRIPT_DLL_API
206      Data(DataAbstract* underlyingdata);
207    
208    
209    /**    /**
210       \brief       \brief
211       Destructor       Destructor
# Line 226  class Data { Line 214  class Data {
214    ~Data();    ~Data();
215    
216    /**    /**
217       \brief       \brief Make this object a deep copy of "other".
      Perform a deep copy.  
218    */    */
219    ESCRIPT_DLL_API    ESCRIPT_DLL_API
220    void    void
221    copy(const Data& other);    copy(const Data& other);
222    
223    /**    /**
224         \brief Return a pointer to a deep copy of this object.
225      */
226      ESCRIPT_DLL_API
227      Data*
228      copySelf();
229    
230    
231    
232    
233      /**
234       Member access methods.       Member access methods.
235    */    */
236    
237    /**    /**
238       \brief       \brief
239       switches on update protection       switches on update protection
240    
241    */    */
242    ESCRIPT_DLL_API    ESCRIPT_DLL_API
# Line 254  class Data { Line 251  class Data {
251    ESCRIPT_DLL_API    ESCRIPT_DLL_API
252    bool    bool
253    isProtected() const;    isProtected() const;
254    
255    /**    /**
256       \brief       \brief
257       Return the values of all data-points as a single python numarray object.       Return the values of a data point on this process
258    */    */
259    ESCRIPT_DLL_API    ESCRIPT_DLL_API
260    const boost::python::numeric::array    const boost::python::numeric::array
261    convertToNumArray();    getValueOfDataPoint(int dataPointNo);
262    
263    /**    /**
264       \brief       \brief
265       Fills the expanded Data object from values of a python numarray object.       sets the values of a data-point from a python object on this process
266    */    */
267    ESCRIPT_DLL_API    ESCRIPT_DLL_API
268    void    void
269    fillFromNumArray(const boost::python::numeric::array);    setValueOfDataPointToPyObject(int dataPointNo, const boost::python::object& py_object);
   
   /**  
      \brief  
      Return the values of a data point on this process  
   */  
   ESCRIPT_DLL_API  
   const boost::python::numeric::array  
   getValueOfDataPoint(int dataPointNo);  
270    
271    /**    /**
272       \brief       \brief
273       sets the values of a data-point on this process       sets the values of a data-point from a numarray object on this process
274    */    */
275    ESCRIPT_DLL_API    ESCRIPT_DLL_API
276    void    void
277    setValueOfDataPointToArray(int dataPointNo, const boost::python::numeric::array);    setValueOfDataPointToArray(int dataPointNo, const boost::python::numeric::array&);
278    
279    /**    /**
280       \brief       \brief
# Line 306  class Data { Line 296  class Data {
296       \brief       \brief
297       Return the tag number associated with the given data-point.       Return the tag number associated with the given data-point.
298    
      The data-point number here corresponds to the data-point number in the  
      numarray returned by convertToNumArray.  
299    */    */
300    ESCRIPT_DLL_API    ESCRIPT_DLL_API
301    int    int
# Line 321  class Data { Line 309  class Data {
309    escriptDataC    escriptDataC
310    getDataC();    getDataC();
311    
312    
313    
314    
315    
316    
317    // REMOVE ME
318    // ESCRIPT_DLL_API
319    // void
320    // CompareDebug(const Data& rd);
321    
322    
323    /**    /**
324       \brief       \brief
325       Return the C wrapper for the Data object - const version.       Return the C wrapper for the Data object - const version.
# Line 331  class Data { Line 330  class Data {
330    
331    /**    /**
332       \brief       \brief
333       Write the data as a string.       Write the data as a string. For large amounts of data, a summary is printed.
334    */    */
335    ESCRIPT_DLL_API    ESCRIPT_DLL_API
   inline  
336    std::string    std::string
337    toString() const    toString() const;
   {  
     return m_data->toString();  
   }  
338    
339    /**  
340       \brief  //  /**
341    /*     \brief
342       Return the DataArrayView of the point data. This essentially contains       Return the DataArrayView of the point data. This essentially contains
343       the shape information for each data point although it also may be used       the shape information for each data point although it also may be used
344       to manipulate the point data.       to manipulate the point data.*/
345    */  //  */
346    ESCRIPT_DLL_API  //   ESCRIPT_DLL_API
347    inline  //   inline
348    const DataArrayView&  //   const DataArrayView&
349    getPointDataView() const  //   getPointDataView() const
350    {  //   {
351       return m_data->getPointDataView();  //      return m_data->getPointDataView();
352    }  //   }
353    
354    /**    /**
355       \brief       \brief
# Line 455  class Data { Line 451  class Data {
451    int    int
452    getDataPointRank() const    getDataPointRank() const
453    {    {
454      return m_data->getPointDataView().getRank();  //    return m_data->getPointDataView().getRank();
455        return m_data->getRank();
456    }    }
457    
458    /**    /**
# Line 492  class Data { Line 489  class Data {
489    {    {
490      return m_data->getNumDPPSample();      return m_data->getNumDPPSample();
491    }    }
492    
493    
494    /**    /**
495       \brief      \brief
496       dumps the object into a netCDF file      Return the number of values in the shape for this object.
497    */    */
498    ESCRIPT_DLL_API    ESCRIPT_DLL_API
499    inline    int
500    void    getNoValues() const
   dump(const std::string fileName) const  
501    {    {
502      return m_data->dump(fileName);      return m_data->getNoValues();
503    }    }
504    
505    
506      /**
507         \brief
508         dumps the object into a netCDF file
509      */
510      ESCRIPT_DLL_API
511      void
512      dump(const std::string fileName) const;
513    /**    /**
514       \brief       \brief
515       Return the sample data for the given sample no. This is not the       Return the sample data for the given sample no. This is not the
# Line 532  class Data { Line 538  class Data {
538      return m_data->getSampleDataByTag(tag);      return m_data->getSampleDataByTag(tag);
539    }    }
540    
541    //  /**
542    /*     \brief
543         Return a view into the data for the data point specified.
544         NOTE: Construction of the DataArrayView is a relatively expensive
545         operation.
546         \param sampleNo - Input -
547         \param dataPointNo - Input -*/
548    //  */
549    //   ESCRIPT_DLL_API
550    //   inline
551    //   DataArrayView
552    //   getDataPoint(int sampleNo,
553    //                int dataPointNo)
554    //   {
555    //                 return m_data->getDataPoint(sampleNo,dataPointNo);
556    //   }
557    
558    
559    /**    /**
560       \brief       \brief
561       Return a view into the data for the data point specified.       Return a view into the data for the data point specified.
# Line 541  class Data { Line 565  class Data {
565       \param dataPointNo - Input -       \param dataPointNo - Input -
566    */    */
567    ESCRIPT_DLL_API    ESCRIPT_DLL_API
568      DataTypes::ValueType::const_reference
569      getDataPoint(int sampleNo, int dataPointNo) const;
570    
571    
572      ESCRIPT_DLL_API
573      DataTypes::ValueType::reference
574      getDataPoint(int sampleNo, int dataPointNo);
575    
576    
577    
578      /**
579         \brief
580         Return the offset for the given sample and point within the sample
581      */
582      ESCRIPT_DLL_API
583    inline    inline
584    DataArrayView    DataTypes::ValueType::size_type
585    getDataPoint(int sampleNo,    getDataOffset(int sampleNo,
586                 int dataPointNo)                 int dataPointNo)
587    {    {
588          return m_data->getDataPoint(sampleNo,dataPointNo);                  return m_data->getPointOffset(sampleNo,dataPointNo);
589    }    }
590    
591    /**    /**
# Line 554  class Data { Line 593  class Data {
593       Return a reference to the data point shape.       Return a reference to the data point shape.
594    */    */
595    ESCRIPT_DLL_API    ESCRIPT_DLL_API
596    const DataArrayView::ShapeType&    inline
597    getDataPointShape() const;    const DataTypes::ShapeType&
598      getDataPointShape() const
599      {
600        return m_data->getShape();
601      }
602    
603    /**    /**
604       \brief       \brief
# Line 579  class Data { Line 622  class Data {
622       Return the number of doubles stored for this Data.       Return the number of doubles stored for this Data.
623    */    */
624    ESCRIPT_DLL_API    ESCRIPT_DLL_API
625    DataArrayView::ValueType::size_type    DataTypes::ValueType::size_type
626    getLength() const;    getLength() const;
627    
628    
629    
630    /**    /**
631       \brief       \brief
632       Assign the given value to the tag. Implicitly converts this       Assign the given value to the tag assocciated with name. Implicitly converts this
633       object to type DataTagged. Throws an exception if this object       object to type DataTagged. Throws an exception if this object
634       cannot be converted to a DataTagged object.       cannot be converted to a DataTagged object or name cannot be mapped onto a tag key.
635         \param tagKey - Input - Integer key.
636         \param value - Input - Value to associate with given key.
637        ==>*
638      */
639      ESCRIPT_DLL_API
640      void
641      setTaggedValueByName(std::string name,
642                           const boost::python::object& value);
643    
644      /**
645         \brief
646         Assign the given value to the tag. Implicitly converts this
647         object to type DataTagged if it is constant.
648    
649       \param tagKey - Input - Integer key.       \param tagKey - Input - Integer key.
650       \param value - Input - Value to associate with given key.       \param value - Input - Value to associate with given key.
651      ==>*      ==>*
# Line 596  class Data { Line 655  class Data {
655    setTaggedValue(int tagKey,    setTaggedValue(int tagKey,
656                   const boost::python::object& value);                   const boost::python::object& value);
657    
658    
659    //  /**
660    //     \brief
661    //     Assign the given value to the tag. Implicitly converts this
662    //     object to type DataTagged if it is constant.
663    //
664    //     \param tagKey - Input - Integer key.
665    //     \param value - Input - Value to associate with given key.
666    //    ==>*
667    //  */
668    //   ESCRIPT_DLL_API
669    //   void
670    //   setTaggedValueFromCPP(int tagKey,
671    //                         const DataArrayView& value);
672    
673    /**    /**
674       \brief       \brief
675       Assign the given value to the tag. Implicitly converts this       Assign the given value to the tag. Implicitly converts this
676       object to type DataTagged. Throws an exception if this object       object to type DataTagged if it is constant.
677       cannot be converted to a DataTagged object.  
678       \param tagKey - Input - Integer key.       \param tagKey - Input - Integer key.
679         \param pointshape - Input - The shape of the value parameter
680       \param value - Input - Value to associate with given key.       \param value - Input - Value to associate with given key.
681      ==>*       \param dataOffset - Input - Offset of the begining of the point within the value parameter
682    */    */
683    ESCRIPT_DLL_API    ESCRIPT_DLL_API
684    void    void
685    setTaggedValueFromCPP(int tagKey,    setTaggedValueFromCPP(int tagKey,
686                          const DataArrayView& value);              const DataTypes::ShapeType& pointshape,
687                            const DataTypes::ValueType& value,
688                int dataOffset=0);
689    
690    
691    
692    /**    /**
693      \brief      \brief
# Line 625  class Data { Line 704  class Data {
704    
705    /**    /**
706       \brief       \brief
707         set all values to zero
708         *
709      */
710      ESCRIPT_DLL_API
711      void
712      setToZero();
713    
714      /**
715         \brief
716       Interpolates this onto the given functionspace and returns       Interpolates this onto the given functionspace and returns
717       the result as a Data object.       the result as a Data object.
718       *       *
# Line 632  class Data { Line 720  class Data {
720    ESCRIPT_DLL_API    ESCRIPT_DLL_API
721    Data    Data
722    interpolate(const FunctionSpace& functionspace) const;    interpolate(const FunctionSpace& functionspace) const;
   
723    /**    /**
724       \brief       \brief
725       Calculates the gradient of the data at the data points of functionspace.       Calculates the gradient of the data at the data points of functionspace.
# Line 729  class Data { Line 816  class Data {
816    
817    /**    /**
818       \brief       \brief
      Return the minimum absolute value of this Data object.  
      *  
   */  
   ESCRIPT_DLL_API  
   double  
   Linf() const;  
   
   /**  
      \brief  
819       Return the maximum value of this Data object.       Return the maximum value of this Data object.
820       *       *
821    */    */
# Line 852  class Data { Line 930  class Data {
930    /**    /**
931       \brief       \brief
932       Return the eigenvalues and corresponding eigenvcetors of the symmetric part at each data point of this Data object.       Return the eigenvalues and corresponding eigenvcetors of the symmetric part at each data point of this Data object.
933       the eigenvalues are ordered in increasing size where eigenvalues with relative difference less than       the eigenvalues are ordered in increasing size where eigenvalues with relative difference less than
934       tol are treated as equal. The eigenvectors are orthogonal, normalized and the sclaed such that the       tol are treated as equal. The eigenvectors are orthogonal, normalized and the sclaed such that the
935       first non-zero entry is positive.       first non-zero entry is positive.
936       Currently this function is restricted to rank 2, square shape, and dimension 3       Currently this function is restricted to rank 2, square shape, and dimension 3
937       *       *
# Line 1057  class Data { Line 1135  class Data {
1135    /**    /**
1136       \brief       \brief
1137       Return the given power of each data point of this boost python object.       Return the given power of each data point of this boost python object.
1138        
1139       \param right Input - the power to raise the object to.       \param right Input - the power to raise the object to.
1140       *       *
1141     */     */
# Line 1068  class Data { Line 1146  class Data {
1146    /**    /**
1147       \brief       \brief
1148       Return the given power of each data point of this boost python object.       Return the given power of each data point of this boost python object.
1149        
1150       \param left Input - the bases       \param left Input - the bases
1151       *       *
1152     */     */
# Line 1104  class Data { Line 1182  class Data {
1182    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1183    Data& operator+=(const boost::python::object& right);    Data& operator+=(const boost::python::object& right);
1184    
1185      ESCRIPT_DLL_API
1186      Data& operator=(const Data& other);
1187    
1188    /**    /**
1189       \brief       \brief
1190       Overloaded operator -=       Overloaded operator -=
# Line 1196  class Data { Line 1277  class Data {
1277    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1278    inline    inline
1279    void    void
1280    unaryOp(UnaryFunction operation);    unaryOp2(UnaryFunction operation);
1281    
1282    /**    /**
1283       \brief       \brief
# Line 1207  class Data { Line 1288  class Data {
1288    */    */
1289    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1290    Data    Data
1291    getSlice(const DataArrayView::RegionType& region) const;    getSlice(const DataTypes::RegionType& region) const;
1292    
1293    /**    /**
1294       \brief       \brief
# Line 1220  class Data { Line 1301  class Data {
1301    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1302    void    void
1303    setSlice(const Data& value,    setSlice(const Data& value,
1304             const DataArrayView::RegionType& region);             const DataTypes::RegionType& region);
1305    
1306    /**    /**
1307       \brief       \brief
1308       Archive the current Data object to the given file.       print the data values to stdout. Used for debugging
      \param fileName - Input - file to archive to.  
1309    */    */
1310    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1311    void    void
1312    archiveData(const std::string fileName);          print(void);
1313    
1314    /**    /**
1315       \brief       \brief
1316       Extract the Data object archived in the given file, overwriting       return the MPI rank number of the local data
1317       the current Data object.                   MPI_COMM_WORLD is assumed and the result of MPI_Comm_size()
1318       Note - the current object must be of type DataEmpty.                   is returned
      \param fileName - Input - file to extract from.  
      \param fspace - Input - a suitable FunctionSpace descibing the data.  
1319    */    */
1320    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1321    void          int
1322    extractData(const std::string fileName,          get_MPIRank(void) const;
               const FunctionSpace& fspace);  
   
1323    
1324    /**    /**
1325       \brief       \brief
1326       print the data values to stdout. Used for debugging       return the MPI rank number of the local data
1327                     MPI_COMM_WORLD is assumed and the result of MPI_Comm_rank()
1328                     is returned
1329    */    */
1330    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1331    void          int
1332      print(void);          get_MPISize(void) const;
1333    
1334    /**    /**
1335       \brief       \brief
1336       return the MPI rank number of the local data       return the MPI rank number of the local data
1337           MPI_COMM_WORLD is assumed and the result of MPI_Comm_size()                   MPI_COMM_WORLD is assumed and returned.
          is returned  
1338    */    */
1339    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1340      int          MPI_Comm
1341      get_MPIRank(void) const;          get_MPIComm(void) const;
1342    
1343    /**    /**
1344       \brief       \brief
1345       return the MPI rank number of the local data       return the object produced by the factory, which is a DataConstant or DataExpanded
1346           MPI_COMM_WORLD is assumed and the result of MPI_Comm_rank()      TODO Ownership of this object should be explained in doco.
          is returned  
1347    */    */
1348    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1349      int          DataAbstract*
1350      get_MPISize(void) const;          borrowData(void) const;
1351    
1352    
1353    /**    /**
1354       \brief       \brief
1355       return the MPI rank number of the local data       Return a pointer to the beginning of the datapoint at the specified offset.
1356           MPI_COMM_WORLD is assumed and returned.       TODO Eventually these should be inlined.
1357         \param i - position(offset) in the underlying datastructure
1358    */    */
1359    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1360      MPI_Comm          DataTypes::ValueType::const_reference
1361      get_MPIComm(void) const;          getDataAtOffset(DataTypes::ValueType::size_type i) const;
1362    
1363    
   /**  
      \brief  
      return the object produced by the factory, which is a DataConstant or DataExpanded  
   */  
1364    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1365      DataAbstract*          DataTypes::ValueType::reference
1366      borrowData(void) const;          getDataAtOffset(DataTypes::ValueType::size_type i);
1367    
1368   protected:   protected:
1369    
# Line 1365  class Data { Line 1440  class Data {
1440       \brief       \brief
1441       Construct a Data object of the appropriate type.       Construct a Data object of the appropriate type.
1442    */    */
1443    template <class IValueType>  
1444    void    void
1445    initialise(const IValueType& value,    initialise(const DataTypes::ValueType& value,
1446             const DataTypes::ShapeType& shape,
1447               const FunctionSpace& what,               const FunctionSpace& what,
1448               bool expanded);               bool expanded);
1449    
1450      void
1451      initialise(const boost::python::numeric::array& value,
1452                     const FunctionSpace& what,
1453                     bool expanded);
1454    
1455    //    //
1456    // flag to protect the data object against any update    // flag to protect the data object against any update
1457    bool m_protected;    bool m_protected;
# Line 1379  class Data { Line 1460  class Data {
1460    // pointer to the actual data object    // pointer to the actual data object
1461    boost::shared_ptr<DataAbstract> m_data;    boost::shared_ptr<DataAbstract> m_data;
1462    
   //  
   // pointer to the internal profiling data  
   struct profDataEntry *profData;  
   
1463  };  };
1464    
1465  template <class IValueType>  
1466  void  
1467  Data::initialise(const IValueType& value,  /**
1468                   const FunctionSpace& what,     Modify a filename for MPI parallel output to multiple files
1469                   bool expanded)  */
1470  {  char *Escript_MPI_appendRankToFileName(const char *, int, int);
   //  
   // Construct a Data object of the appropriate type.  
   // Construct the object first as there seems to be a bug which causes  
   // undefined behaviour if an exception is thrown during construction  
   // within the shared_ptr constructor.  
   if (expanded) {  
     DataAbstract* temp=new DataExpanded(value,what);  
     boost::shared_ptr<DataAbstract> temp_data(temp);  
     m_data=temp_data;  
   } else {  
     DataAbstract* temp=new DataConstant(value,what);  
     boost::shared_ptr<DataAbstract> temp_data(temp);  
     m_data=temp_data;  
   }  
 }  
1471    
1472  /**  /**
1473     Binary Data object operators.     Binary Data object operators.
1474  */  */
1475  inline double rpow(double x,double y)  inline double rpow(double x,double y)
1476  {  {
1477      return pow(y,x);      return pow(y,x);
1478  };  }
1479    
1480  /**  /**
1481    \brief    \brief
# Line 1507  ESCRIPT_DLL_API Data operator*(const boo Line 1569  ESCRIPT_DLL_API Data operator*(const boo
1569  */  */
1570  ESCRIPT_DLL_API Data operator/(const boost::python::object& left, const Data& right);  ESCRIPT_DLL_API Data operator/(const boost::python::object& left, const Data& right);
1571    
1572    
1573    
1574  /**  /**
1575    \brief    \brief
1576    Output operator    Output operator
# Line 1528  C_GeneralTensorProduct(Data& arg0, Line 1592  C_GeneralTensorProduct(Data& arg0,
1592                       int axis_offset=0,                       int axis_offset=0,
1593                       int transpose=0);                       int transpose=0);
1594    
1595  /**  
1596    \brief  
1597    // /**
1598    /*  \brief
1599    Return true if operands are equivalent, else return false.    Return true if operands are equivalent, else return false.
1600    NB: this operator does very little at this point, and isn't to    NB: this operator does very little at this point, and isn't to
1601    be relied on. Requires further implementation.    be relied on. Requires further implementation.*/
1602  */  //*/
1603  //ESCRIPT_DLL_API bool operator==(const Data& left, const Data& right);  // ESCRIPT_DLL_API bool operator==(const Data& left, const Data& right);
1604    
1605  /**  /**
1606    \brief    \brief
# Line 1549  Data::binaryOp(const Data& right, Line 1615  Data::binaryOp(const Data& right,
1615  {  {
1616     //     //
1617     // if this has a rank of zero promote it to the rank of the RHS     // if this has a rank of zero promote it to the rank of the RHS
1618     if (getPointDataView().getRank()==0 && right.getPointDataView().getRank()!=0) {     if (getDataPointRank()==0 && right.getDataPointRank()!=0) {
1619       throw DataException("Error - attempt to update rank zero object with object with rank bigger than zero.");       throw DataException("Error - attempt to update rank zero object with object with rank bigger than zero.");
1620     }     }
1621     //     //
# Line 1558  Data::binaryOp(const Data& right, Line 1624  Data::binaryOp(const Data& right,
1624     if (getFunctionSpace()!=right.getFunctionSpace()) {     if (getFunctionSpace()!=right.getFunctionSpace()) {
1625       if (right.probeInterpolation(getFunctionSpace())) {       if (right.probeInterpolation(getFunctionSpace())) {
1626         //         //
1627         // an interpolation is required so create a new Data         // an interpolation is required so create a new Data
1628         tempRight=Data(right,this->getFunctionSpace());         tempRight=Data(right,this->getFunctionSpace());
1629       } else if (probeInterpolation(right.getFunctionSpace())) {       } else if (probeInterpolation(right.getFunctionSpace())) {
1630         //         //
# Line 1602  Data::binaryOp(const Data& right, Line 1668  Data::binaryOp(const Data& right,
1668       EsysAssert((leftC!=0 && rightC!=0), "Programming error - casting to DataConstant.");       EsysAssert((leftC!=0 && rightC!=0), "Programming error - casting to DataConstant.");
1669       escript::binaryOp(*leftC,*rightC,operation);       escript::binaryOp(*leftC,*rightC,operation);
1670     }     }
    #if defined DOPROF  
    profData->binary++;  
    #endif  
 }  
   
 /**  
   \brief  
   Perform the given unary operation on other and return the result.  
   Given operation is performed on each element of each data point, thus  
   argument object is a rank n Data object, and returned object is a rank n  
   Data object.  
   Calls Data::unaryOp.  
 */  
 template <class UnaryFunction>  
 inline  
 Data  
 unaryOp(const Data& other,  
         UnaryFunction operation)  
 {  
   Data result;  
   result.copy(other);  
   result.unaryOp(operation);  
   return result;  
 }  
   
 /**  
   \brief  
   Perform the given unary operation on this.  
   Given operation is performed on each element of each data point.  
   Calls escript::unaryOp.  
 */  
 template <class UnaryFunction>  
 inline  
 void  
 Data::unaryOp(UnaryFunction operation)  
 {  
   if (isExpanded()) {  
     DataExpanded* leftC=dynamic_cast<DataExpanded*>(m_data.get());  
     EsysAssert((leftC!=0), "Programming error - casting to DataExpanded.");  
     escript::unaryOp(*leftC,operation);  
   } else if (isTagged()) {  
     DataTagged* leftC=dynamic_cast<DataTagged*>(m_data.get());  
     EsysAssert((leftC!=0), "Programming error - casting to DataTagged.");  
     escript::unaryOp(*leftC,operation);  
   } else if (isConstant()) {  
     DataConstant* leftC=dynamic_cast<DataConstant*>(m_data.get());  
     EsysAssert((leftC!=0), "Programming error - casting to DataConstant.");  
     escript::unaryOp(*leftC,operation);  
   }  
1671  }  }
1672    
1673  /**  /**
# Line 1685  Data::algorithm(BinaryFunction operation Line 1702  Data::algorithm(BinaryFunction operation
1702    \brief    \brief
1703    Perform the given data point reduction algorithm on data and return the result.    Perform the given data point reduction algorithm on data and return the result.
1704    Given operation combines each element within each data point into a scalar,    Given operation combines each element within each data point into a scalar,
1705    thus argument object is a rank n Data object, and returned object is a    thus argument object is a rank n Data object, and returned object is a
1706    rank 0 Data object.    rank 0 Data object.
1707    Calls escript::dp_algorithm.    Calls escript::dp_algorithm.
1708  */  */
# Line 1695  Data Line 1712  Data
1712  Data::dp_algorithm(BinaryFunction operation, double initial_value) const  Data::dp_algorithm(BinaryFunction operation, double initial_value) const
1713  {  {
1714    if (isExpanded()) {    if (isExpanded()) {
1715      Data result(0,DataArrayView::ShapeType(),getFunctionSpace(),isExpanded());      Data result(0,DataTypes::ShapeType(),getFunctionSpace(),isExpanded());
1716      DataExpanded* dataE=dynamic_cast<DataExpanded*>(m_data.get());      DataExpanded* dataE=dynamic_cast<DataExpanded*>(m_data.get());
1717      DataExpanded* resultE=dynamic_cast<DataExpanded*>(result.m_data.get());      DataExpanded* resultE=dynamic_cast<DataExpanded*>(result.m_data.get());
1718      EsysAssert((dataE!=0), "Programming error - casting data to DataExpanded.");      EsysAssert((dataE!=0), "Programming error - casting data to DataExpanded.");
# Line 1704  Data::dp_algorithm(BinaryFunction operat Line 1721  Data::dp_algorithm(BinaryFunction operat
1721      return result;      return result;
1722    } else if (isTagged()) {    } else if (isTagged()) {
1723      DataTagged* dataT=dynamic_cast<DataTagged*>(m_data.get());      DataTagged* dataT=dynamic_cast<DataTagged*>(m_data.get());
     DataArrayView::ShapeType viewShape;  
     DataArrayView::ValueType viewData(1);  
     viewData[0]=0;  
     DataArrayView defaultValue(viewData,viewShape);  
     DataTagged::TagListType keys;  
     DataTagged::ValueListType values;  
     DataTagged::DataMapType::const_iterator i;  
     for (i=dataT->getTagLookup().begin();i!=dataT->getTagLookup().end();i++) {  
       keys.push_back(i->first);  
       values.push_back(defaultValue);  
     }  
     Data result(keys,values,defaultValue,getFunctionSpace());  
     DataTagged* resultT=dynamic_cast<DataTagged*>(result.m_data.get());  
1724      EsysAssert((dataT!=0), "Programming error - casting data to DataTagged.");      EsysAssert((dataT!=0), "Programming error - casting data to DataTagged.");
1725      EsysAssert((resultT!=0), "Programming error - casting result to DataTagged.");  
1726    //     DataTypes::ShapeType viewShape;
1727    //     DataTypes::ValueType viewData(1);
1728    //     viewData[0]=0;
1729    //     DataArrayView defaultValue(viewData,viewShape);
1730    //     DataTagged::TagListType keys;
1731    //     DataTagged::ValueListType values;
1732    //     DataTagged::DataMapType::const_iterator i;
1733    //     for (i=dataT->getTagLookup().begin();i!=dataT->getTagLookup().end();i++) {
1734    //       keys.push_back(i->first);
1735    //       values.push_back(defaultValue);
1736    //     }
1737    //     Data result(keys,values,defaultValue,getFunctionSpace());
1738    //     DataTagged* resultT=dynamic_cast<DataTagged*>(result.m_data.get());
1739    //     EsysAssert((resultT!=0), "Programming error - casting result to DataTagged.");
1740    
1741    
1742    
1743    
1744        DataTypes::ValueType defval(1);
1745        defval[0]=0;
1746        DataTagged* resultT=new DataTagged(getFunctionSpace(), DataTypes::scalarShape, defval, dataT);
1747      escript::dp_algorithm(*dataT,*resultT,operation,initial_value);      escript::dp_algorithm(*dataT,*resultT,operation,initial_value);
1748      return result;      return Data(resultT);   // note: the Data object now owns the resultT pointer
1749    
1750    } else if (isConstant()) {    } else if (isConstant()) {
1751      Data result(0,DataArrayView::ShapeType(),getFunctionSpace(),isExpanded());      Data result(0,DataTypes::ShapeType(),getFunctionSpace(),isExpanded());
1752      DataConstant* dataC=dynamic_cast<DataConstant*>(m_data.get());      DataConstant* dataC=dynamic_cast<DataConstant*>(m_data.get());
1753      DataConstant* resultC=dynamic_cast<DataConstant*>(result.m_data.get());      DataConstant* resultC=dynamic_cast<DataConstant*>(result.m_data.get());
1754      EsysAssert((dataC!=0), "Programming error - casting data to DataConstant.");      EsysAssert((dataC!=0), "Programming error - casting data to DataConstant.");
# Line 1734  Data::dp_algorithm(BinaryFunction operat Line 1760  Data::dp_algorithm(BinaryFunction operat
1760    return falseRetVal;    return falseRetVal;
1761  }  }
1762    
1763    /**
1764      \brief
1765      Compute a tensor operation with two Data objects
1766      \param arg0 - Input - Data object
1767      \param arg1 - Input - Data object
1768      \param operation - Input - Binary op functor
1769    */
1770    template <typename BinaryFunction>
1771    inline
1772    Data
1773    C_TensorBinaryOperation(Data const &arg_0,
1774                            Data const &arg_1,
1775                            BinaryFunction operation)
1776    {
1777      // Interpolate if necessary and find an appropriate function space
1778      Data arg_0_Z, arg_1_Z;
1779      if (arg_0.getFunctionSpace()!=arg_1.getFunctionSpace()) {
1780        if (arg_0.probeInterpolation(arg_1.getFunctionSpace())) {
1781          arg_0_Z = arg_0.interpolate(arg_1.getFunctionSpace());
1782          arg_1_Z = Data(arg_1);
1783        }
1784        else if (arg_1.probeInterpolation(arg_0.getFunctionSpace())) {
1785          arg_1_Z=arg_1.interpolate(arg_0.getFunctionSpace());
1786          arg_0_Z =Data(arg_0);
1787        }
1788        else {
1789          throw DataException("Error - C_TensorBinaryOperation: arguments have incompatible function spaces.");
1790        }
1791      } else {
1792          arg_0_Z = Data(arg_0);
1793          arg_1_Z = Data(arg_1);
1794      }
1795      // Get rank and shape of inputs
1796      int rank0 = arg_0_Z.getDataPointRank();
1797      int rank1 = arg_1_Z.getDataPointRank();
1798      DataTypes::ShapeType shape0 = arg_0_Z.getDataPointShape();
1799      DataTypes::ShapeType shape1 = arg_1_Z.getDataPointShape();
1800      int size0 = arg_0_Z.getDataPointSize();
1801      int size1 = arg_1_Z.getDataPointSize();
1802    
1803      // Declare output Data object
1804      Data res;
1805    
1806      if (shape0 == shape1) {
1807    
1808        if (arg_0_Z.isConstant()   && arg_1_Z.isConstant()) {
1809          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());      // DataConstant output
1810    /*      double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[0]);
1811          double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[0]);
1812          double *ptr_2 = &((res.getPointDataView().getData())[0]);*/
1813          double *ptr_0 = &(arg_0_Z.getDataAtOffset(0));
1814          double *ptr_1 = &(arg_1_Z.getDataAtOffset(0));
1815          double *ptr_2 = &(res.getDataAtOffset(0));
1816    
1817          tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1818        }
1819        else if (arg_0_Z.isConstant()   && arg_1_Z.isTagged()) {
1820    
1821          // Prepare the DataConstant input
1822          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
1823    
1824          // Borrow DataTagged input from Data object
1825          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
1826    
1827          // Prepare a DataTagged output 2
1828          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());      // DataTagged output
1829          res.tag();
1830          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
1831    
1832          // Prepare offset into DataConstant
1833          int offset_0 = tmp_0->getPointOffset(0,0);
1834          double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
1835          // Get the views
1836    //       DataArrayView view_1 = tmp_1->getDefaultValue();
1837    //       DataArrayView view_2 = tmp_2->getDefaultValue();
1838    //       // Get the pointers to the actual data
1839    //       double *ptr_1 = &((view_1.getData())[0]);
1840    //       double *ptr_2 = &((view_2.getData())[0]);
1841    
1842          // Get the pointers to the actual data
1843          double *ptr_1 = &(tmp_1->getDefaultValue(0));
1844          double *ptr_2 = &(tmp_2->getDefaultValue(0));
1845    
1846          // Compute a result for the default
1847          tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1848          // Compute a result for each tag
1849          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
1850          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
1851          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
1852            tmp_2->addTag(i->first);
1853    /*        DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
1854            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
1855            double *ptr_1 = &view_1.getData(0);
1856            double *ptr_2 = &view_2.getData(0);*/
1857            double *ptr_1 = &(tmp_1->getDataByTag(i->first,0));
1858            double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
1859    
1860            tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1861          }
1862    
1863        }
1864        else if (arg_0_Z.isConstant()   && arg_1_Z.isExpanded()) {
1865    
1866          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
1867          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
1868          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
1869          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
1870    
1871          int sampleNo_1,dataPointNo_1;
1872          int numSamples_1 = arg_1_Z.getNumSamples();
1873          int numDataPointsPerSample_1 = arg_1_Z.getNumDataPointsPerSample();
1874          int offset_0 = tmp_0->getPointOffset(0,0);
1875          #pragma omp parallel for private(sampleNo_1,dataPointNo_1) schedule(static)
1876          for (sampleNo_1 = 0; sampleNo_1 < numSamples_1; sampleNo_1++) {
1877            for (dataPointNo_1 = 0; dataPointNo_1 < numDataPointsPerSample_1; dataPointNo_1++) {
1878              int offset_1 = tmp_1->getPointOffset(sampleNo_1,dataPointNo_1);
1879              int offset_2 = tmp_2->getPointOffset(sampleNo_1,dataPointNo_1);
1880    //           double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
1881    //           double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
1882    //           double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
1883    
1884              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
1885              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
1886              double *ptr_2 = &(res.getDataAtOffset(offset_2));
1887              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1888            }
1889          }
1890    
1891        }
1892        else if (arg_0_Z.isTagged()     && arg_1_Z.isConstant()) {
1893    
1894          // Borrow DataTagged input from Data object
1895          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
1896    
1897          // Prepare the DataConstant input
1898          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
1899    
1900          // Prepare a DataTagged output 2
1901          res = Data(0.0, shape0, arg_0_Z.getFunctionSpace());      // DataTagged output
1902          res.tag();
1903          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
1904    
1905          // Prepare offset into DataConstant
1906          int offset_1 = tmp_1->getPointOffset(0,0);
1907    //       double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
1908          double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
1909          // Get the views
1910    //       DataArrayView view_0 = tmp_0->getDefaultValue();
1911    //       DataArrayView view_2 = tmp_2->getDefaultValue();
1912    //       // Get the pointers to the actual data
1913    //       double *ptr_0 = &((view_0.getData())[0]);
1914    //       double *ptr_2 = &((view_2.getData())[0]);
1915          // Get the pointers to the actual data
1916          double *ptr_0 = &(tmp_0->getDefaultValue(0));
1917          double *ptr_2 = &(tmp_2->getDefaultValue(0));
1918          // Compute a result for the default
1919          tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1920          // Compute a result for each tag
1921          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
1922          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
1923          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
1924            tmp_2->addTag(i->first);
1925    //         DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
1926    //         DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
1927    //         double *ptr_0 = &view_0.getData(0);
1928    //         double *ptr_2 = &view_2.getData(0);
1929            double *ptr_0 = &(tmp_0->getDataByTag(i->first,0));
1930            double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
1931            tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1932          }
1933    
1934        }
1935        else if (arg_0_Z.isTagged()     && arg_1_Z.isTagged()) {
1936    
1937          // Borrow DataTagged input from Data object
1938          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
1939    
1940          // Borrow DataTagged input from Data object
1941          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
1942    
1943          // Prepare a DataTagged output 2
1944          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());
1945          res.tag();        // DataTagged output
1946          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
1947    
1948    //       // Get the views
1949    //       DataArrayView view_0 = tmp_0->getDefaultValue();
1950    //       DataArrayView view_1 = tmp_1->getDefaultValue();
1951    //       DataArrayView view_2 = tmp_2->getDefaultValue();
1952    //       // Get the pointers to the actual data
1953    //       double *ptr_0 = &((view_0.getData())[0]);
1954    //       double *ptr_1 = &((view_1.getData())[0]);
1955    //       double *ptr_2 = &((view_2.getData())[0]);
1956    
1957          // Get the pointers to the actual data
1958          double *ptr_0 = &(tmp_0->getDefaultValue(0));
1959          double *ptr_1 = &(tmp_1->getDefaultValue(0));
1960          double *ptr_2 = &(tmp_2->getDefaultValue(0));
1961    
1962          // Compute a result for the default
1963          tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1964          // Merge the tags
1965          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
1966          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
1967          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
1968          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
1969            tmp_2->addTag(i->first); // use tmp_2 to get correct shape
1970          }
1971          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
1972            tmp_2->addTag(i->first);
1973          }
1974          // Compute a result for each tag
1975          const DataTagged::DataMapType& lookup_2=tmp_2->getTagLookup();
1976          for (i=lookup_2.begin();i!=lookup_2.end();i++) {
1977    
1978    //         DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
1979    //         DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
1980    //         DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
1981    //         double *ptr_0 = &view_0.getData(0);
1982    //         double *ptr_1 = &view_1.getData(0);
1983    //         double *ptr_2 = &view_2.getData(0);
1984    
1985            double *ptr_0 = &(tmp_0->getDataByTag(i->first,0));
1986            double *ptr_1 = &(tmp_1->getDataByTag(i->first,0));
1987            double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
1988    
1989            tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1990          }
1991    
1992        }
1993        else if (arg_0_Z.isTagged()     && arg_1_Z.isExpanded()) {
1994    
1995          // After finding a common function space above the two inputs have the same numSamples and num DPPS
1996          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
1997          DataTagged*   tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
1998          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
1999          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2000    
2001          int sampleNo_0,dataPointNo_0;
2002          int numSamples_0 = arg_0_Z.getNumSamples();
2003          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2004          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2005          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2006            int offset_0 = tmp_0->getPointOffset(sampleNo_0,0); // They're all the same, so just use #0
2007            double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2008            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2009              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2010              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2011    
2012    //           double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2013    //           double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2014              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2015              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2016    
2017    
2018              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
2019            }
2020          }
2021    
2022        }
2023        else if (arg_0_Z.isExpanded()   && arg_1_Z.isConstant()) {
2024    
2025          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2026          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2027          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
2028          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2029    
2030          int sampleNo_0,dataPointNo_0;
2031          int numSamples_0 = arg_0_Z.getNumSamples();
2032          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2033          int offset_1 = tmp_1->getPointOffset(0,0);
2034          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2035          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2036            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2037              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2038              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2039    
2040    //           double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2041    //           double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2042    //           double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2043    
2044              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2045              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2046              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2047    
2048    
2049              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
2050            }
2051          }
2052    
2053        }
2054        else if (arg_0_Z.isExpanded()   && arg_1_Z.isTagged()) {
2055    
2056          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2057          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2058          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2059          DataTagged*   tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2060          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2061    
2062          int sampleNo_0,dataPointNo_0;
2063          int numSamples_0 = arg_0_Z.getNumSamples();
2064          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2065          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2066          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2067            int offset_1 = tmp_1->getPointOffset(sampleNo_0,0);
2068            double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2069            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2070              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2071              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2072              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2073              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2074              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
2075            }
2076          }
2077    
2078        }
2079        else if (arg_0_Z.isExpanded()   && arg_1_Z.isExpanded()) {
2080    
2081          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2082          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2083          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2084          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2085          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2086    
2087          int sampleNo_0,dataPointNo_0;
2088          int numSamples_0 = arg_0_Z.getNumSamples();
2089          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2090          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2091          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2092            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2093              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2094              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2095              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2096              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2097              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2098              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2099              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
2100            }
2101          }
2102    
2103        }
2104        else {
2105          throw DataException("Error - C_TensorBinaryOperation: unknown combination of inputs");
2106        }
2107    
2108      } else if (0 == rank0) {
2109    
2110        if (arg_0_Z.isConstant()   && arg_1_Z.isConstant()) {
2111          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace());      // DataConstant output
2112          double *ptr_0 = &(arg_0_Z.getDataAtOffset(0));
2113          double *ptr_1 = &(arg_1_Z.getDataAtOffset(0));
2114          double *ptr_2 = &(res.getDataAtOffset(0));
2115          tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2116        }
2117        else if (arg_0_Z.isConstant()   && arg_1_Z.isTagged()) {
2118    
2119          // Prepare the DataConstant input
2120          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
2121    
2122          // Borrow DataTagged input from Data object
2123          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2124    
2125          // Prepare a DataTagged output 2
2126          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace());      // DataTagged output
2127          res.tag();
2128          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2129    
2130          // Prepare offset into DataConstant
2131          int offset_0 = tmp_0->getPointOffset(0,0);
2132          double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2133          // Get the views
2134    //       DataArrayView view_1 = tmp_1->getDefaultValue();
2135    //       DataArrayView view_2 = tmp_2->getDefaultValue();
2136    //       // Get the pointers to the actual data
2137    //       double *ptr_1 = &((view_1.getData())[0]);
2138    //       double *ptr_2 = &((view_2.getData())[0]);
2139           double *ptr_1 = &(tmp_1->getDefaultValue(0));
2140           double *ptr_2 = &(tmp_2->getDefaultValue(0));
2141    
2142          // Compute a result for the default
2143          tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2144          // Compute a result for each tag
2145          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
2146          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2147          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
2148            tmp_2->addTag(i->first);
2149    //         DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
2150    //         DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2151    //         double *ptr_1 = &view_1.getData(0);
2152    //         double *ptr_2 = &view_2.getData(0);
2153            double *ptr_1 = &(tmp_1->getDataByTag(i->first,0));
2154            double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
2155            tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2156          }
2157    
2158        }
2159        else if (arg_0_Z.isConstant()   && arg_1_Z.isExpanded()) {
2160    
2161          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2162          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
2163          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2164          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2165    
2166          int sampleNo_1,dataPointNo_1;
2167          int numSamples_1 = arg_1_Z.getNumSamples();
2168          int numDataPointsPerSample_1 = arg_1_Z.getNumDataPointsPerSample();
2169          int offset_0 = tmp_0->getPointOffset(0,0);
2170          #pragma omp parallel for private(sampleNo_1,dataPointNo_1) schedule(static)
2171          for (sampleNo_1 = 0; sampleNo_1 < numSamples_1; sampleNo_1++) {
2172            for (dataPointNo_1 = 0; dataPointNo_1 < numDataPointsPerSample_1; dataPointNo_1++) {
2173              int offset_1 = tmp_1->getPointOffset(sampleNo_1,dataPointNo_1);
2174              int offset_2 = tmp_2->getPointOffset(sampleNo_1,dataPointNo_1);
2175              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2176              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2177              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2178              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2179    
2180            }
2181          }
2182    
2183        }
2184        else if (arg_0_Z.isTagged()     && arg_1_Z.isConstant()) {
2185    
2186          // Borrow DataTagged input from Data object
2187          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2188    
2189          // Prepare the DataConstant input
2190          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
2191    
2192          // Prepare a DataTagged output 2
2193          res = Data(0.0, shape1, arg_0_Z.getFunctionSpace());      // DataTagged output
2194          res.tag();
2195          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2196    
2197          // Prepare offset into DataConstant
2198          int offset_1 = tmp_1->getPointOffset(0,0);
2199    //       double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2200          double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2201          // Get the views
2202    /*      DataArrayView view_0 = tmp_0->getDefaultValue();
2203          DataArrayView view_2 = tmp_2->getDefaultValue();
2204          // Get the pointers to the actual data
2205          double *ptr_0 = &((view_0.getData())[0]);
2206          double *ptr_2 = &((view_2.getData())[0]);*/
2207    
2208          // Get the pointers to the actual data
2209          double *ptr_0 = &(tmp_0->getDefaultValue(0));
2210          double *ptr_2 = &(tmp_2->getDefaultValue(0));
2211    
2212    
2213          // Compute a result for the default
2214          tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2215          // Compute a result for each tag
2216          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2217          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2218          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2219            tmp_2->addTag(i->first);
2220    /*        DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2221            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2222            double *ptr_0 = &view_0.getData(0);
2223            double *ptr_2 = &view_2.getData(0);*/
2224            double *ptr_0 = &(tmp_0->getDataByTag(i->first,0));
2225            double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
2226    
2227            tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2228          }
2229    
2230        }
2231        else if (arg_0_Z.isTagged()     && arg_1_Z.isTagged()) {
2232    
2233          // Borrow DataTagged input from Data object
2234          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2235    
2236          // Borrow DataTagged input from Data object
2237          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2238    
2239          // Prepare a DataTagged output 2
2240          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace());
2241          res.tag();        // DataTagged output
2242          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2243    
2244          // Get the views
2245    /*      DataArrayView view_0 = tmp_0->getDefaultValue();
2246          DataArrayView view_1 = tmp_1->getDefaultValue();
2247          DataArrayView view_2 = tmp_2->getDefaultValue();
2248          // Get the pointers to the actual data
2249          double *ptr_0 = &((view_0.getData())[0]);
2250          double *ptr_1 = &((view_1.getData())[0]);
2251          double *ptr_2 = &((view_2.getData())[0]);*/
2252    
2253          // Get the pointers to the actual data
2254          double *ptr_0 = &(tmp_0->getDefaultValue(0));
2255          double *ptr_1 = &(tmp_1->getDefaultValue(0));
2256          double *ptr_2 = &(tmp_2->getDefaultValue(0));
2257    
2258    
2259          // Compute a result for the default
2260          tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2261          // Merge the tags
2262          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2263          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2264          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
2265          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2266            tmp_2->addTag(i->first); // use tmp_2 to get correct shape
2267          }
2268          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
2269            tmp_2->addTag(i->first);
2270          }
2271          // Compute a result for each tag
2272          const DataTagged::DataMapType& lookup_2=tmp_2->getTagLookup();
2273          for (i=lookup_2.begin();i!=lookup_2.end();i++) {
2274    
2275    /*        DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2276            DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
2277            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2278            double *ptr_0 = &view_0.getData(0);
2279            double *ptr_1 = &view_1.getData(0);
2280            double *ptr_2 = &view_2.getData(0);*/
2281    
2282            double *ptr_0 = &(tmp_0->getDataByTag(i->first,0));
2283            double *ptr_1 = &(tmp_1->getDataByTag(i->first,0));
2284            double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
2285    
2286            tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2287          }
2288    
2289        }
2290        else if (arg_0_Z.isTagged()     && arg_1_Z.isExpanded()) {
2291    
2292          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2293          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2294          DataTagged*   tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2295          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2296          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2297    
2298          int sampleNo_0,dataPointNo_0;
2299          int numSamples_0 = arg_0_Z.getNumSamples();
2300          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2301          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2302          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2303            int offset_0 = tmp_0->getPointOffset(sampleNo_0,0); // They're all the same, so just use #0
2304            double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2305            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2306              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2307              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2308              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2309              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2310              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2311            }
2312          }
2313    
2314        }
2315        else if (arg_0_Z.isExpanded()   && arg_1_Z.isConstant()) {
2316    
2317          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2318          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2319          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
2320          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2321    
2322          int sampleNo_0,dataPointNo_0;
2323          int numSamples_0 = arg_0_Z.getNumSamples();
2324          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2325          int offset_1 = tmp_1->getPointOffset(0,0);
2326          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2327          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2328            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2329              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2330              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2331              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2332              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2333              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2334              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2335            }
2336          }
2337    
2338    
2339        }
2340        else if (arg_0_Z.isExpanded()   && arg_1_Z.isTagged()) {
2341    
2342          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2343          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2344          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2345          DataTagged*   tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2346          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2347    
2348          int sampleNo_0,dataPointNo_0;
2349          int numSamples_0 = arg_0_Z.getNumSamples();
2350          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2351          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2352          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2353            int offset_1 = tmp_1->getPointOffset(sampleNo_0,0);
2354            double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2355            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2356              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2357              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2358              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2359              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2360              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2361            }
2362          }
2363    
2364        }
2365        else if (arg_0_Z.isExpanded()   && arg_1_Z.isExpanded()) {
2366    
2367          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2368          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2369          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2370          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2371          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2372    
2373          int sampleNo_0,dataPointNo_0;
2374          int numSamples_0 = arg_0_Z.getNumSamples();
2375          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2376          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2377          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2378            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2379              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2380              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2381              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2382              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2383              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2384              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2385              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2386            }
2387          }
2388    
2389        }
2390        else {
2391          throw DataException("Error - C_TensorBinaryOperation: unknown combination of inputs");
2392        }
2393    
2394      } else if (0 == rank1) {
2395    
2396        if (arg_0_Z.isConstant()   && arg_1_Z.isConstant()) {
2397          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());      // DataConstant output
2398          double *ptr_0 = &(arg_0_Z.getDataAtOffset(0));
2399          double *ptr_1 = &(arg_1_Z.getDataAtOffset(0));
2400          double *ptr_2 = &(res.getDataAtOffset(0));
2401          tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2402        }
2403        else if (arg_0_Z.isConstant()   && arg_1_Z.isTagged()) {
2404    
2405          // Prepare the DataConstant input
2406          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
2407    
2408          // Borrow DataTagged input from Data object
2409          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2410    
2411          // Prepare a DataTagged output 2
2412          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());      // DataTagged output
2413          res.tag();
2414          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2415    
2416          // Prepare offset into DataConstant
2417          int offset_0 = tmp_0->getPointOffset(0,0);
2418          double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2419          // Get the views
2420    /*      DataArrayView view_1 = tmp_1->getDefaultValue();
2421          DataArrayView view_2 = tmp_2->getDefaultValue();
2422          // Get the pointers to the actual data
2423          double *ptr_1 = &((view_1.getData())[0]);
2424          double *ptr_2 = &((view_2.getData())[0]);*/
2425          //Get the pointers to the actual data
2426          double *ptr_1 = &(tmp_1->getDefaultValue(0));
2427          double *ptr_2 = &(tmp_2->getDefaultValue(0));
2428    
2429          // Compute a result for the default
2430          tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2431          // Compute a result for each tag
2432          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
2433          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2434          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
2435            tmp_2->addTag(i->first);
2436    //         DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
2437    //         DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2438    //         double *ptr_1 = &view_1.getData(0);
2439    //         double *ptr_2 = &view_2.getData(0);
2440            double *ptr_1 = &(tmp_1->getDataByTag(i->first,0));
2441            double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
2442    
2443    
2444            tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2445          }
2446    
2447        }
2448        else if (arg_0_Z.isConstant()   && arg_1_Z.isExpanded()) {
2449    
2450          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2451          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
2452          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2453          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2454    
2455          int sampleNo_1,dataPointNo_1;
2456          int numSamples_1 = arg_1_Z.getNumSamples();
2457          int numDataPointsPerSample_1 = arg_1_Z.getNumDataPointsPerSample();
2458          int offset_0 = tmp_0->getPointOffset(0,0);
2459          #pragma omp parallel for private(sampleNo_1,dataPointNo_1) schedule(static)
2460          for (sampleNo_1 = 0; sampleNo_1 < numSamples_1; sampleNo_1++) {
2461            for (dataPointNo_1 = 0; dataPointNo_1 < numDataPointsPerSample_1; dataPointNo_1++) {
2462              int offset_1 = tmp_1->getPointOffset(sampleNo_1,dataPointNo_1);
2463              int offset_2 = tmp_2->getPointOffset(sampleNo_1,dataPointNo_1);
2464              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2465              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2466              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2467              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2468            }
2469          }
2470    
2471        }
2472        else if (arg_0_Z.isTagged()     && arg_1_Z.isConstant()) {
2473    
2474          // Borrow DataTagged input from Data object
2475          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2476    
2477          // Prepare the DataConstant input
2478          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
2479    
2480          // Prepare a DataTagged output 2
2481          res = Data(0.0, shape0, arg_0_Z.getFunctionSpace());      // DataTagged output
2482          res.tag();
2483          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2484    
2485          // Prepare offset into DataConstant
2486          int offset_1 = tmp_1->getPointOffset(0,0);
2487          double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2488          // Get the views
2489    //       DataArrayView view_0 = tmp_0->getDefaultValue();
2490    //       DataArrayView view_2 = tmp_2->getDefaultValue();
2491    //       // Get the pointers to the actual data
2492    //       double *ptr_0 = &((view_0.getData())[0]);
2493    //       double *ptr_2 = &((view_2.getData())[0]);
2494          // Get the pointers to the actual data
2495          double *ptr_0 = &(tmp_0->getDefaultValue(0));
2496          double *ptr_2 = &(tmp_2->getDefaultValue(0));
2497          // Compute a result for the default
2498          tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2499          // Compute a result for each tag
2500          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2501          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2502          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2503            tmp_2->addTag(i->first);
2504    /*        DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2505            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2506            double *ptr_0 = &view_0.getData(0);
2507            double *ptr_2 = &view_2.getData(0);*/
2508            double *ptr_0 = &(tmp_0->getDataByTag(i->first,0));
2509            double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
2510            tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2511          }
2512    
2513        }
2514        else if (arg_0_Z.isTagged()     && arg_1_Z.isTagged()) {
2515    
2516          // Borrow DataTagged input from Data object
2517          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2518    
2519          // Borrow DataTagged input from Data object
2520          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2521    
2522          // Prepare a DataTagged output 2
2523          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());
2524          res.tag();        // DataTagged output
2525          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2526    
2527          // Get the views
2528    //       DataArrayView view_0 = tmp_0->getDefaultValue();
2529    //       DataArrayView view_1 = tmp_1->getDefaultValue();
2530    //       DataArrayView view_2 = tmp_2->getDefaultValue();
2531    //       // Get the pointers to the actual data
2532    //       double *ptr_0 = &((view_0.getData())[0]);
2533    //       double *ptr_1 = &((view_1.getData())[0]);
2534    //       double *ptr_2 = &((view_2.getData())[0]);
2535    
2536          // Get the pointers to the actual data
2537          double *ptr_0 = &(tmp_0->getDefaultValue(0));
2538          double *ptr_1 = &(tmp_1->getDefaultValue(0));
2539          double *ptr_2 = &(tmp_2->getDefaultValue(0));
2540    
2541          // Compute a result for the default
2542          tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2543          // Merge the tags
2544          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2545          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2546          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
2547          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2548            tmp_2->addTag(i->first); // use tmp_2 to get correct shape
2549          }
2550          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
2551            tmp_2->addTag(i->first);
2552          }
2553          // Compute a result for each tag
2554          const DataTagged::DataMapType& lookup_2=tmp_2->getTagLookup();
2555          for (i=lookup_2.begin();i!=lookup_2.end();i++) {
2556    //         DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2557    //         DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
2558    //         DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2559    //         double *ptr_0 = &view_0.getData(0);
2560    //         double *ptr_1 = &view_1.getData(0);
2561    //         double *ptr_2 = &view_2.getData(0);
2562    
2563            double *ptr_0 = &(tmp_0->getDataByTag(i->first,0));
2564            double *ptr_1 = &(tmp_1->getDataByTag(i->first,0));
2565            double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
2566            tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2567          }
2568    
2569        }
2570        else if (arg_0_Z.isTagged()     && arg_1_Z.isExpanded()) {
2571    
2572          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2573          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2574          DataTagged*   tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2575          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2576          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2577    
2578          int sampleNo_0,dataPointNo_0;
2579          int numSamples_0 = arg_0_Z.getNumSamples();
2580          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2581          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2582          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2583            int offset_0 = tmp_0->getPointOffset(sampleNo_0,0); // They're all the same, so just use #0
2584            double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2585            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2586              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2587              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2588              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2589              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2590              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2591            }
2592          }
2593    
2594        }
2595        else if (arg_0_Z.isExpanded()   && arg_1_Z.isConstant()) {
2596    
2597          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2598          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2599          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
2600          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2601    
2602          int sampleNo_0,dataPointNo_0;
2603          int numSamples_0 = arg_0_Z.getNumSamples();
2604          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2605          int offset_1 = tmp_1->getPointOffset(0,0);
2606          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2607          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2608            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2609              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2610              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2611              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2612              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2613              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2614              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2615            }
2616          }
2617    
2618    
2619        }
2620        else if (arg_0_Z.isExpanded()   && arg_1_Z.isTagged()) {
2621    
2622          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2623          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2624          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2625          DataTagged*   tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2626          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2627    
2628          int sampleNo_0,dataPointNo_0;
2629          int numSamples_0 = arg_0_Z.getNumSamples();
2630          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2631          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2632          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2633            int offset_1 = tmp_1->getPointOffset(sampleNo_0,0);
2634            double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2635            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2636              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2637              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2638              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2639              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2640              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2641            }
2642          }
2643    
2644        }
2645        else if (arg_0_Z.isExpanded()   && arg_1_Z.isExpanded()) {
2646    
2647          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2648          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2649          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2650          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2651          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2652    
2653          int sampleNo_0,dataPointNo_0;
2654          int numSamples_0 = arg_0_Z.getNumSamples();
2655          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2656          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2657          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2658            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2659              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2660              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2661              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2662              double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2663              double *ptr_1 = &(arg_1_Z.getDataAtOffset(offset_1));
2664              double *ptr_2 = &(res.getDataAtOffset(offset_2));
2665              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2666            }
2667          }
2668    
2669        }
2670        else {
2671          throw DataException("Error - C_TensorBinaryOperation: unknown combination of inputs");
2672        }
2673    
2674      } else {
2675        throw DataException("Error - C_TensorBinaryOperation: arguments have incompatible shapes");
2676      }
2677    
2678      return res;
2679    }
2680    
2681    template <typename UnaryFunction>
2682    Data
2683    C_TensorUnaryOperation(Data const &arg_0,
2684                           UnaryFunction operation)
2685    {
2686      // Interpolate if necessary and find an appropriate function space
2687      Data arg_0_Z = Data(arg_0);
2688    
2689      // Get rank and shape of inputs
2690      int rank0 = arg_0_Z.getDataPointRank();
2691      const DataTypes::ShapeType& shape0 = arg_0_Z.getDataPointShape();
2692      int size0 = arg_0_Z.getDataPointSize();
2693    
2694      // Declare output Data object
2695      Data res;
2696    
2697      if (arg_0_Z.isConstant()) {
2698        res = Data(0.0, shape0, arg_0_Z.getFunctionSpace());      // DataConstant output
2699    //     double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[0]);
2700    //     double *ptr_2 = &((res.getPointDataView().getData())[0]);
2701        double *ptr_0 = &(arg_0_Z.getDataAtOffset(0));
2702        double *ptr_2 = &(res.getDataAtOffset(0));
2703        tensor_unary_operation(size0, ptr_0, ptr_2, operation);
2704      }
2705      else if (arg_0_Z.isTagged()) {
2706    
2707        // Borrow DataTagged input from Data object
2708        DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2709    
2710        // Prepare a DataTagged output 2
2711        res = Data(0.0, shape0, arg_0_Z.getFunctionSpace());   // DataTagged output
2712        res.tag();
2713        DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2714    
2715    //     // Get the views
2716    //     DataArrayView view_0 = tmp_0->getDefaultValue();
2717    //     DataArrayView view_2 = tmp_2->getDefaultValue();
2718    //     // Get the pointers to the actual data
2719    //     double *ptr_0 = &((view_0.getData())[0]);
2720    //     double *ptr_2 = &((view_2.getData())[0]);
2721        // Get the pointers to the actual data
2722        double *ptr_0 = &(tmp_0->getDefaultValue(0));
2723        double *ptr_2 = &(tmp_2->getDefaultValue(0));
2724        // Compute a result for the default
2725        tensor_unary_operation(size0, ptr_0, ptr_2, operation);
2726        // Compute a result for each tag
2727        const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2728        DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2729        for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2730          tmp_2->addTag(i->first);
2731    //       DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2732    //       DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2733    //       double *ptr_0 = &view_0.getData(0);
2734    //       double *ptr_2 = &view_2.getData(0);
2735          double *ptr_0 = &(tmp_0->getDataByTag(i->first,0));
2736          double *ptr_2 = &(tmp_2->getDataByTag(i->first,0));
2737          tensor_unary_operation(size0, ptr_0, ptr_2, operation);
2738        }
2739    
2740      }
2741      else if (arg_0_Z.isExpanded()) {
2742    
2743        res = Data(0.0, shape0, arg_0_Z.getFunctionSpace(),true); // DataExpanded output
2744        DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2745        DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2746    
2747        int sampleNo_0,dataPointNo_0;
2748        int numSamples_0 = arg_0_Z.getNumSamples();
2749        int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2750        #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2751        for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2752          for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2753    //         int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2754    //         int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2755    //         double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2756    //         double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2757            int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2758            int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2759            double *ptr_0 = &(arg_0_Z.getDataAtOffset(offset_0));
2760            double *ptr_2 = &(res.getDataAtOffset(offset_2));
2761            tensor_unary_operation(size0, ptr_0, ptr_2, operation);
2762          }
2763        }
2764    
2765      }
2766      else {
2767        throw DataException("Error - C_TensorUnaryOperation: unknown combination of inputs");
2768      }
2769    
2770      return res;
2771    }
2772    
2773  }  }
2774  #endif  #endif

Legend:
Removed from v.971  
changed lines
  Added in v.1802

  ViewVC Help
Powered by ViewVC 1.1.26