// The if statements impose a total order on the selection types, and call this function // with arguments swapped if they are out of this order. This simplifies the above helper // functions. ADIOS_SELECTION * adios_selection_intersect_global(const ADIOS_SELECTION *s1, const ADIOS_SELECTION *s2) { if (!is_global_selection(s1) || !is_global_selection(s2)) { adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Internal error: adios_selection_intersect_global called on non-global selection(s)"); return NULL; } switch (s1->type) { case ADIOS_SELECTION_BOUNDINGBOX: { const ADIOS_SELECTION_BOUNDINGBOX_STRUCT *bb1 = &s1->u.bb; return adios_selection_intersect_bb(bb1, s2); } case ADIOS_SELECTION_POINTS: { const ADIOS_SELECTION_POINTS_STRUCT *pts1 = &s1->u.points; if (s1->type == ADIOS_SELECTION_BOUNDINGBOX) { return adios_selection_intersect_global(s2, s1); } else { return adios_selection_intersect_pts(pts1, s2); } } default: adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Unknown selection type %d", s1->type); return NULL; } }
inline static uint64_t adios_patch_data_to_wb(void *dst, uint64_t dst_ragged_offset, const ADIOS_SELECTION_WRITEBLOCK_STRUCT *dst_wb, void *src, uint64_t src_ragged_offset, const ADIOS_SELECTION *src_sel, const ADIOS_SELECTION_BOUNDINGBOX_STRUCT *vb_bounds, enum ADIOS_DATATYPES datum_type, enum ADIOS_FLAG swap_endianness) { switch (src_sel->type) { case ADIOS_SELECTION_BOUNDINGBOX: { const ADIOS_SELECTION_BOUNDINGBOX_STRUCT *src_bb = &src_sel->u.bb; return adios_patch_data_bb_to_wb(dst, dst_ragged_offset, dst_wb, src, src_ragged_offset, src_bb, vb_bounds, datum_type, swap_endianness); } case ADIOS_SELECTION_POINTS: { const ADIOS_SELECTION_POINTS_STRUCT *src_pts = &src_sel->u.points; return adios_patch_data_pts_to_wb(dst, dst_ragged_offset, dst_wb, src, src_ragged_offset, src_pts, vb_bounds, datum_type, swap_endianness); } case ADIOS_SELECTION_WRITEBLOCK: { const ADIOS_SELECTION_WRITEBLOCK_STRUCT *src_wb = &src_sel->u.block; return adios_patch_data_wb_to_wb(dst, dst_ragged_offset, dst_wb, src, src_ragged_offset, src_wb, vb_bounds, datum_type, swap_endianness); } case ADIOS_SELECTION_AUTO: adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Incompatible selection types %d, %d were used while patching decoded " "transformed data into the user buffer (this is an error in the current " "transform plugin)", src_sel->type, ADIOS_SELECTION_BOUNDINGBOX); return 0; default: adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Unknown selection type %d", src_sel->type); return 0; } }
uint64_t adios_patch_data_to_global(void *dst, uint64_t dst_ragged_offset, const ADIOS_SELECTION *dst_sel, void *src, uint64_t src_ragged_offset, const ADIOS_SELECTION *src_sel, enum ADIOS_DATATYPES datum_type, enum ADIOS_FLAG swap_endianness) { if (!is_global_selection(dst_sel) || !is_global_selection(src_sel)) { adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Internal error: adios_patch_data_to_global called on non-global selection type(s)"); return 0; } switch (dst_sel->type) { case ADIOS_SELECTION_BOUNDINGBOX: { const ADIOS_SELECTION_BOUNDINGBOX_STRUCT *dst_bb = &dst_sel->u.bb; return adios_patch_data_to_bb(dst, dst_ragged_offset, dst_bb, src, src_ragged_offset, src_sel, datum_type, swap_endianness); } case ADIOS_SELECTION_POINTS: { const ADIOS_SELECTION_POINTS_STRUCT *dst_pts = &dst_sel->u.points; return adios_patch_data_to_pts(dst, dst_ragged_offset, dst_pts, src, src_ragged_offset, src_sel, datum_type, swap_endianness); } default: adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Unknown selection type %d", dst_sel->type); return 0; } }
static uint64_t compute_selection_size_in_bytes(const ADIOS_SELECTION *sel, enum ADIOS_DATATYPES datum_type, int timestep, const ADIOS_VARINFO *raw_varinfo, const ADIOS_TRANSINFO *transinfo) { int typesize = adios_get_type_size(datum_type, NULL); int i; switch (sel->type) { case ADIOS_SELECTION_BOUNDINGBOX: { const ADIOS_SELECTION_BOUNDINGBOX_STRUCT *bb = &sel->u.bb; const int ndim = bb->ndim; uint64_t size = typesize; for (i = 0; i < ndim; i++) size *= bb->count[i]; return size; } case ADIOS_SELECTION_POINTS: { const ADIOS_SELECTION_POINTS_STRUCT *pts = &sel->u.points; return pts->ndim * pts->npoints * typesize; } case ADIOS_SELECTION_WRITEBLOCK: { const ADIOS_SELECTION_WRITEBLOCK_STRUCT *wb = &sel->u.block; if (wb->is_sub_pg_selection) { return wb->nelements * typesize; } else { const ADIOS_VARBLOCK *theblock; uint64_t size = typesize; int absolute_idx; if (wb->is_absolute_index) { absolute_idx = wb->index; } else { int timestep_start_idx = 0; for (i = 0; i < timestep; i++) timestep_start_idx += raw_varinfo->nblocks[i]; absolute_idx = timestep_start_idx + wb->index; } theblock = &transinfo->orig_blockinfo[absolute_idx]; for (i = 0; i < transinfo->orig_ndim; i++) size *= theblock->count[i]; return size; } } case ADIOS_SELECTION_AUTO: default: adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Unsupported selection type %d in data transform read layer", sel->type); return 0; } }
// NOTE: vb_bounds_sel is the bounding box of the varblock to which dst_sel (a writeblock selection) corresponds uint64_t adios_patch_data_to_local(void *dst, uint64_t dst_ragged_offset, const ADIOS_SELECTION *dst_sel, void *src, uint64_t src_ragged_offset, const ADIOS_SELECTION *src_sel, const ADIOS_SELECTION_BOUNDINGBOX_STRUCT *vb_bounds, enum ADIOS_DATATYPES datum_type, enum ADIOS_FLAG swap_endianness) { if (is_global_selection(dst_sel)) { adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Internal error: adios_patch_data_to_local called on non-global destination selection type %d", dst_sel->type); return 0; } switch (dst_sel->type) { case ADIOS_SELECTION_WRITEBLOCK: { const ADIOS_SELECTION_WRITEBLOCK_STRUCT *dst_wb = &dst_sel->u.block; return adios_patch_data_to_wb(dst, dst_ragged_offset, dst_wb, src, src_ragged_offset, src_sel, vb_bounds, datum_type, swap_endianness); } default: adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Unknown selection type %d", dst_sel->type); return 0; } }
// s2 can be any selection type except boundingbox inline static ADIOS_SELECTION * adios_selection_intersect_pts(const ADIOS_SELECTION_POINTS_STRUCT *pts1, const ADIOS_SELECTION *s2) { switch (s2->type) { case ADIOS_SELECTION_POINTS: { const ADIOS_SELECTION_POINTS_STRUCT *pts2 = &s2->u.points; return adios_selection_intersect_pts_pts(pts1, pts2); } default: adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Unknown selection type %d", s2->type); return NULL; } }
// // Any-on-any local intersection function // ADIOS_SELECTION * adios_selection_intersect_local(const ADIOS_SELECTION *s1, const ADIOS_SELECTION *s2, int timestep, const ADIOS_VARINFO *raw_varinfo, const ADIOS_TRANSINFO *transinfo) { if (is_global_selection(s1) || is_global_selection(s2)) { adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Internal error: adios_selection_intersect_local called on non-local selection(s)"); return NULL; } switch (s1->type) { case ADIOS_SELECTION_WRITEBLOCK: { const ADIOS_SELECTION_WRITEBLOCK_STRUCT *wb1 = &s1->u.block; return adios_selection_intersect_wb(wb1, s2, timestep, raw_varinfo, transinfo); } case ADIOS_SELECTION_AUTO: { adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Unsupported selection type AUTO in adios_selection_intersect_local"); return NULL; } default: adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Unknown selection type %d", s1->type); return NULL; } }
/* * Takes a datablock and applies its data to the user buffer for the given * read request group, then frees the given datablock. Assumes there is, in * fact, a user buffer (i.e., it is not NULL). * * Assumes that the datablock selection is of type bounding box. * * NOTE: also frees the data buffer within the datablock * * @return non-zero if some data in the datablock intersected the read * request's selection, and was applied; returns 0 otherwise. */ static int apply_datablock_to_result_and_free(adios_datablock *datablock, adios_transform_read_request *reqgroup) { assert(datablock); assert(reqgroup); assert(reqgroup->orig_sel); assert(reqgroup->orig_data); void *output_buffer; if (is_global_selection(reqgroup->orig_sel)) { // All timestep results have the same size in bytes, so offset the // output pointer by however many timesteps precede this timestep const int timestep_within_request = datablock->timestep - reqgroup->from_steps; output_buffer = (char*)reqgroup->orig_data + timestep_within_request * reqgroup->orig_sel_timestep_size; } else if (reqgroup->orig_sel->type == ADIOS_SELECTION_WRITEBLOCK) { // For writeblock selections, computing the output buffer position for // the current timestep is a bit trickier, since varblocks may be // different sizes in different timesteps const ADIOS_SELECTION_WRITEBLOCK_STRUCT *orig_sel_wb = &reqgroup->orig_sel->u.block; // Compute the offset into the output buffer at which the current timestep's output should go // For absolute writeblocks, it's always 0, since there is only 1 timestep involved. For // timestep-relative writeblock selections, we add the size of the writeblock (i.e. varblock) // for all previous timesteps in the user's read request. uint64_t output_buffer_offset = 0; if (!orig_sel_wb->is_absolute_index) { int timestep; for (timestep = reqgroup->from_steps; timestep < datablock->timestep; timestep++) { // Compute the size of the writeblock at this timestep and add it to our offset output_buffer_offset += compute_selection_size_in_bytes(reqgroup->orig_sel, reqgroup->transinfo->orig_type, timestep, reqgroup->raw_varinfo, reqgroup->transinfo); } } // Offset the output pointer by the computed amount output_buffer = (char*)reqgroup->orig_data + output_buffer_offset; } else { adios_error_at_line(err_unspecified, __FILE__, __LINE__, "Internal error: unexpected selection type %d, this should not be possible here\n", reqgroup->orig_sel->type); } // Now that we have the output buffer pointer, actually perform the patch operation const uint64_t used_count = apply_datablock_to_buffer_and_free( reqgroup->raw_varinfo, reqgroup->transinfo, datablock, &output_buffer, reqgroup->orig_sel, NULL /* don't need the intersection */, reqgroup->swap_endianness); return used_count != 0; }
// s2 can be selection type inline static ADIOS_SELECTION * adios_selection_intersect_wb(const ADIOS_SELECTION_WRITEBLOCK_STRUCT *wb1, const ADIOS_SELECTION *s2, int timestep, const ADIOS_VARINFO *raw_varinfo, const ADIOS_TRANSINFO *transinfo) { switch (s2->type) { case ADIOS_SELECTION_WRITEBLOCK: { const ADIOS_SELECTION_WRITEBLOCK_STRUCT *wb2 = &s2->u.block; return adios_selection_intersect_wb_wb(wb1, wb2, timestep, raw_varinfo, transinfo); } default: adios_error_at_line(err_invalid_argument, __FILE__, __LINE__, "Unknown selection type %d", s2->type); return NULL; } }
// Note: from_steps and nsteps are ignored in the absolute writeblock case static void populate_read_request_for_local_selection( const ADIOS_VARINFO *raw_varinfo, const ADIOS_TRANSINFO *transinfo, const ADIOS_SELECTION *sel, int from_steps, int nsteps, adios_transform_read_request *readreq) { int timestep, timestep_blockidx, blockidx; if (sel->type == ADIOS_SELECTION_WRITEBLOCK) { const ADIOS_SELECTION_WRITEBLOCK_STRUCT *wb = &sel->u.block; if (wb->is_absolute_index) { // For an absolute writeblock, at most one PG is touched (0 if erroneous blockidx) blockidx = wb->index; // Convert blockidx to timestep and timestep_blockidx int valid_blockidx = compute_relative_blockidx_from_absolute_blockidx(raw_varinfo, blockidx, ×tep, ×tep_blockidx); if (valid_blockidx) { generate_read_request_for_pg(raw_varinfo, transinfo, sel, timestep, timestep_blockidx, blockidx, readreq); } else { adios_error(err_invalid_timestep, "Writeblock selection with invalid absolute index %d passed to adios_schedule_read, caught in ADIOS transforms layer", wb->index); } } else { // For a relative writeblock, one PG may be touched per timestep in the user's timestep range timestep_blockidx = wb->index; for (timestep = from_steps; timestep < from_steps + nsteps; timestep++) { // Convert timestep (loop iterator variable) and timestep_blockidx to blockidx int valid_blockidx = compute_absolute_blockidx_from_relative_blockidx(raw_varinfo, timestep, timestep_blockidx, &blockidx); if (valid_blockidx) { generate_read_request_for_pg(raw_varinfo, transinfo, sel, timestep, timestep_blockidx, blockidx, readreq); } else { adios_error(err_invalid_timestep, "Writeblock selection with index %d passed to adios_schedule_read is invalid in timestep %d, caught in ADIOS transforms layer", wb->index, timestep); } } } } else { adios_error_at_line(err_operation_not_supported, __FILE__, __LINE__, "Internal error: unsupported selection type %d in populate_read_request_for_local_selection", sel->type); } }
/* * Takes a datablock and applies its data to a given output buffer (described * by a given output buffer selection), then frees the given datablock. * * If *output_buffer is non-NULL, copies the pertinent data from datablock->data * into *output_buffer, assuming *output_buffer is shaped like output_sel. * * If *output_buffer is NULL, allocates a minimum-size buffer to contain the * data for the *intersection* of datablock->bounds and output_sel and returns * it via *output_buffer. * * If out_inter_sel != NULL, returns a selection representing the intersection * of datablock->bounds and output_sel (i.e., the portion of data that was * actually copied) via *out_inter_sel. Otherwise, leaves this argument untouched. * * This function can handle any combination of datablock bounding selection and * output buffer bounding selection: * - If both datablock and buffer selections are global (BB, points), performs * a straightforward data patching based on the geometry * - If both datablock and buffer selections are local (writeblock), performs * a straghtforward memcpy as appropriate * - If the buffer is global but the datablock is local, promotes the datablock * to a bounding box using blockinfo (note: the variable is guaranteed to be * a global array in this case because the user supplied a global selection) * - If the buffer is local but the datablock is global, promotes the buffer * to a bounding box using blockinfo (note: the variable may or may not be * a global array in this case; however, even if it is not, both the datablock * and the buffer will be constructed relative to (0,0,...,0), so it will do * the right thing). * * @return the number of elements patched into the output buffer (0 indicates * no data in the given datablock was pertinent to the given output * buffer selection) */ static uint64_t apply_datablock_to_buffer_and_free( const ADIOS_VARINFO *raw_varinfo, const ADIOS_TRANSINFO *transinfo, adios_datablock *datablock, void **output_buffer, const ADIOS_SELECTION *output_sel, ADIOS_SELECTION **out_inter_sel, enum ADIOS_FLAG swap_endianness) { uint64_t used_count = 0; ADIOS_SELECTION *inter_sel = NULL; assert(raw_varinfo && transinfo && datablock && output_buffer && output_sel); // Check preconditions if (datablock->bounds->type != ADIOS_SELECTION_BOUNDINGBOX && datablock->bounds->type != ADIOS_SELECTION_POINTS && datablock->bounds->type != ADIOS_SELECTION_WRITEBLOCK) { adios_error(err_operation_not_supported, "Only results of bounding box, points, or writeblock selection types " "are currently accepted from transform plugins (received selection type %d)\n", datablock->bounds->type); return 0; } if (output_sel->type != ADIOS_SELECTION_BOUNDINGBOX && output_sel->type != ADIOS_SELECTION_POINTS && output_sel->type != ADIOS_SELECTION_WRITEBLOCK) { adios_error_at_line(err_operation_not_supported, __FILE__, __LINE__, "Internal error: only bounding box, points, or writeblock selection types " "are currently supported in apply_datablock_to_buffer_and_free (received selection type %d)\n", datablock->bounds->type); return 0; } // Invoke the appropriate helper function depending // on whether at least one of datablock->bounds or // output_sel is global if (!is_global_selection(datablock->bounds) && !is_global_selection(output_sel)) { used_count = apply_datablock_to_buffer_local_selections( raw_varinfo, transinfo, datablock, output_buffer, output_sel, &inter_sel, (out_inter_sel ? 1 : 0), swap_endianness); } else { used_count = apply_datablock_to_buffer_nonlocal_selections( raw_varinfo, transinfo, datablock, output_buffer, output_sel, &inter_sel, (out_inter_sel ? 1 : 0), swap_endianness); } // Clean up the returned intersection if it is not wanted by the caller if (inter_sel) { if (out_inter_sel) { *out_inter_sel = inter_sel; } else { // TODO: Deep delete the selection (delete points list, start/count arrays, etc.) a2sel_free(inter_sel); } } // Free the datablock adios_datablock_free(&datablock, 1); return used_count; }