1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
 int
 saf_write_topo_relation(SAF_ParMode pmode,      /* The parallel mode. */
                         SAF_Rel *rel,           /* The relation handle. */
                         hid_t A_type,           /* See saf_declare_topo_relation(). */
                         void *A_buf,            /* See saf_declare_topo_relation(). */
                         hid_t B_type,           /* See saf_declare_topo_relation(). */
                         void *B_buf,            /* See saf_declare_topo_relation(). */
                         SAF_Db *file            /* The optional destination file. By default (if null) the data is written to
                                                  * the same file to which REL belongs. */
                         )
 {
     SAF_ENTER(saf_write_topo_relation, SAF_PRECONDITION_ERROR);
     double              timer_start=0;          /* Start time for keeping track of how long it takes to write data. */
     ss_scope_t          scope;                  /* Scope where relation's blob will be created. */
     ss_cat_t            storage_decomp;         /* The sub_decomp_cat or storage_decomp cached from REL for convenience. */
     ss_collection_t     storage_coll;           /* The collection associated with the storage_decomp category of REL. */
     size_t              bufferSize;             /* Number of elements in a buffer. */
     ss_set_t            sup;                    /* Cached superset from REL. */

     SAF_REQUIRE(_saf_valid_pmode(pmode), SAF_LOW_CHK_COST, SAF_PRECONDITION_ERROR,
                 _saf_errmsg("PMODE must be valid"));
     if (!_saf_is_participating_proc(pmode)) SAF_RETURN(-1);

     SAF_REQUIRE(SS_REL(rel), SAF_LOW_CHK_COST, SAF_PRECONDITION_ERROR,
                 _saf_errmsg("REL must be a valid rel handle"));
     SAF_REQUIRE(A_type || !A_buf, SAF_LOW_CHK_COST, SAF_PRECONDITION_ERROR,
                 _saf_errmsg("A_TYPE must be supplied if A_BUF is supplied"));
     SAF_REQUIRE(_saf_valid_topo_write_buffers(pmode, rel, A_type, A_buf, B_type, B_buf),
                 SAF_HIGH_CHK_COST, SAF_PRECONDITION_ERROR,
                 _saf_errmsg("A- and B-buffers and types must be set appropriately"));

     if (_SAF_GLOBALS.p.TraceTimes)
         timer_start = _saf_wall_clock(false);

     /* Cache some stuff for convenience */
     sup = SS_REL(rel)->sup;
     storage_decomp = SS_REL(rel)->sub_decomp_cat;

     /* Where sould a new blob be created if we have to do that? */
     ss_pers_scope(file?(ss_pers_t*)file:(ss_pers_t*)rel, &scope);

     /* Pick up the A- and B-buffer pointers and types, note that these may be found either in the relation handle (when they
      * were supplied to the saf_declare_topo_relation() function) or as parameters passed to this function. */
     if (!A_buf) A_buf = SS_REL(rel)->m.abuf;
     if (A_type<=0) A_type = SS_REL(rel)->m.abuf_type;
     if (!B_buf) B_buf = SS_REL(rel)->m.bbuf;
     if (B_type<=0) B_type = SS_REL(rel)->m.bbuf_type;

     /* If the relation is stored on a decomposition, then the category is stored as the "sub_decomp_cat", we'll use this cat */
     if (_saf_is_self_decomp(&storage_decomp)) {
         /* Compute the B-buffer size... */
         switch (SS_RELREP(SS_REL_P(rel,rep_type))->id) {
         case SAF_STRUCTURED_ID:
             /* We assume rectangular structure and, thus, there is no data to write. */
             goto theExit;
         case SAF_UNSTRUCTURED_ID:
             /* In this case the size of the B-buffer depends on the contents of the A-buffer. If an A-buffer was provided in
              * the declare then the B-buffer size would already be known.  But if the A-buffer was deferred by the declare and
              * provided here then the B-buffer buffer size only refkects the size of the collection and must be computed with
              * the number provided as the single element of the A-buffer. */
             if (SS_REL(rel)->m.abuf) {
                 bufferSize = SS_REL(rel)->m.bbuf_size;
             } else {
                 int A_buf_int;
                 _saf_convert(A_type, A_buf, H5T_NATIVE_INT, &A_buf_int);
                 bufferSize = SS_REL(rel)->m.bbuf_size * A_buf_int;
             }

             /* Now it is time to write the B-buffer (range) contents out and record it as a blob.  First write out the B-buffer
              * data. Disallow overwrite of topology relation */
             if (!SS_PERS_ISNULL(SS_REL_P(rel,r_blob)))
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot overwrite topology relation"));

             /* Meaning of pmode...
              *   SAF_ALL:  Collective call where all tasks have identical data and therefore only one of them needs to write
              *             to the file.
              *   SAF_EACH: Collective call where the N tasks create N blobs but those blobs all point into a single common
              *             dataset where the data is stored in task rank order. */
             SAF_DIRTY(rel,pmode);
             if (NULL==ss_blob_new(&scope, SAF_ALL==pmode?SS_ALLSAME:0U, SS_REL_P(rel,r_blob)) ||
                 ss_blob_bind_m1(SS_REL_P(rel,r_blob), B_buf, B_type, (hsize_t)bufferSize)<0 ||
                 ss_blob_mkstorage(SS_REL_P(rel,r_blob), NULL, SAF_ALL==pmode?SS_ALLSAME:SS_BLOB_EACH, NULL)<0)
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot create range blob"));
             if (ss_blob_write1(SS_REL_P(rel,r_blob), (hsize_t)0, (hsize_t)bufferSize,
                                SS_BLOB_UNBIND|SS_BLOB_COLLECTIVE|(SAF_ALL==pmode?SS_ALLSAME:0U), NULL)<0)
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot write to range blob"));
             break;
         case SAF_ARBITRARY_ID:
             /* In this case the size of the B-buffer depends on the contents of the A-buffer.  If an A-buffer was provided in
              * the declare then the B-buffer size would already be known.  But the A-buffer was not provided in the declare so
              * we'll now compute the size of the B-buffer by summing the contents of the A-buffer.  Note that the A-buffer
              * "size" was known (and is the collection size). */
             if (SS_REL(rel)->m.bbuf_size!=SS_NOSIZE) {
                 bufferSize = SS_REL(rel)->m.bbuf_size;
             } else {
                 int A_buf_int;
                 size_t i, A_type_size=H5Tget_size(A_type);
                 for (i=0, bufferSize=0; i<SS_REL(rel)->m.abuf_size; i++) {
                     _saf_convert(A_type, (char*)A_buf+i*A_type_size, H5T_NATIVE_INT, &A_buf_int);
                     bufferSize += A_buf_int;
                 }
             }

             /* First write out the A-buffer data. Disallow overwrite of topology relation. */
             if (!SS_PERS_ISNULL(SS_REL_P(rel,d_blob)))
                 SAF_ERROR(SAF_FILE_ERROR,_saf_errmsg("cannot overwrite topology relation"));
             /* Meaning of pmode...
              *   SAF_ALL:  Collective call where all tasks have identical data and therefore only one of them needs to write
              *             to the file.
              *   SAF_EACH: Collective call where the N tasks create N blobs but those blobs all point into a single common
              *             dataset where the data is stored in task rank order. */
             SAF_DIRTY(rel,pmode);
             if (NULL==ss_blob_new(&scope, SAF_ALL==pmode?SS_ALLSAME:0U, SS_REL_P(rel,d_blob)))
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot create domain blob"));
             if (ss_blob_bind_m1(SS_REL_P(rel,d_blob), A_buf, A_type, (hsize_t)SS_REL(rel)->m.abuf_size)<0)
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot bind memory to domain blob"));
             if (ss_blob_mkstorage(SS_REL_P(rel,d_blob), NULL, SAF_ALL==pmode?SS_ALLSAME:SS_BLOB_EACH, NULL)<0)
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot create domain blob dataset"));
             if (ss_blob_write1(SS_REL_P(rel,d_blob), (hsize_t)0, (hsize_t)SS_REL(rel)->m.abuf_size,
                                SS_BLOB_UNBIND|SS_BLOB_COLLECTIVE|(SAF_ALL==pmode?SS_ALLSAME:0U), NULL)<0)
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot write to domain blob"));

             /* Now write out the B-buffer data. Disallow overwrite of topology relation. */
             if (!SS_PERS_ISNULL(SS_REL_P(rel,r_blob)))
                 SAF_ERROR(SAF_FILE_ERROR,_saf_errmsg("cannot overwrite topology relation"));
             /* Meaning of pmode...
              *   SAF_ALL:  Collective call where all tasks have identical data and therefore only one of them needs to write
              *             to the file.
              *   SAF_EACH: Collective call where the N tasks create N blobs but those blobs all point into a single common
              *             dataset where the data is stored in task rank order. */
             SAF_DIRTY(rel,pmode);
             if (NULL==ss_blob_new(&scope, SAF_ALL==pmode?SS_ALLSAME:0U, SS_REL_P(rel,r_blob)))
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot create range blob"));
             if (ss_blob_bind_m1(SS_REL_P(rel,r_blob), B_buf, B_type, (hsize_t)bufferSize)<0)
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot bind memory to range blob"));
             if (ss_blob_mkstorage(SS_REL_P(rel,r_blob), NULL, SAF_ALL==pmode?SS_ALLSAME:SS_BLOB_EACH, NULL)<0)
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot create range blob dataset"));
             if (ss_blob_write1(SS_REL_P(rel,r_blob), (hsize_t)0, (hsize_t)bufferSize,
                                SS_BLOB_UNBIND|SS_BLOB_COLLECTIVE|(SAF_ALL==pmode?SS_ALLSAME:0U), NULL)<0)
                 SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot write to range blob"));
             break;
         default:
             SAF_ERROR(SAF_FILE_ERROR,_saf_errmsg("invalid topological relation rep-type in database"));
         }
     } else {
         /* The relation is stored on a decomposition. The caller should have passed a single buffer (A-buffer) of handles. */
         if (NULL==_saf_getCollection_set(&sup, &storage_decomp, &storage_coll))
             SAF_ERROR(SAF_CONSTRAINT_ERROR,
                       _saf_errmsg("collection \"%s\" not found on set \"%s\"",
                                   ss_string_ptr(SS_CAT_P(&storage_decomp,name)), ss_string_ptr(SS_SET_P(&sup,name))));
         bufferSize = SS_COLLECTION(&storage_coll)->count;

         /* Disallow overwrites of topology */
         if (ss_array_nelmts(SS_REL_P(rel,indirect_rels))>0 || !SS_PERS_ISNULL(SS_REL_P(rel,d_blob)))
             SAF_ERROR(SAF_FILE_ERROR, _saf_errmsg("cannot overwrite topology relation"));

         /* Copy relation links from A_buf into the indirect_rels variable length array. */
 #ifdef SSLIB_SUPPORT_PENDING
         /* Does SAF_EACH mode mean that each task is providing data to be stored in task rank order? */
 #endif /*SSLIB_SUPPORT_PENDING*/
         SAF_DIRTY(rel, pmode);
         ss_array_resize(SS_REL_P(rel,indirect_rels), bufferSize);
         ss_array_put(SS_REL_P(rel,indirect_rels), ss_pers_tm, (size_t)0, bufferSize, A_buf);
     }

 theExit:

     if (_SAF_GLOBALS.p.TraceTimes)
         _SAF_GLOBALS.CummWriteTime += (_saf_wall_clock(false) - timer_start);

     SAF_LEAVE(SAF_SUCCESS);
 }