rungekutta.clΒΆ

///
/// @file rungekutta.cl
///
/// Adaptive 1st or 2nd order Runge-Kutta single-stepping functions
///
/// @author CPS
/// @bug No known bugs
///

///
/// @defgroup integrationfns Runge-Kutta integration step functions
/// Functions used to compute Runge-Kutta integration down and up streamlines
///

#ifdef KERNEL_INTEGRATE_TRAJECTORY
///
/// Compute a single step of 2nd-order Runge-Kutta numerical integration of
///    a streamline given precomputed 1st and 2nd order step vectors.
/// If the step is deemed too small, return @p true = stuck;
///    otherwise return @p false = ok.
/// Record this step in the global @p trajectory_vec (array of step vectors).
/// Also update the total streamline length @p l_trajectory and
///    increment the step counter @p n_steps.
/// In addition, push the current @p vec to the @p prev_vec, push the current @p idx to
///    the @p prev_idx, and update the @p idx of the current @p vec.
///
/// Compiled if KERNEL_INTEGRATE_TRAJECTORY is defined.
///
/// @param[in,out]  dt: delta time step
/// @param[in,out]  dl: step distance
/// @param[in,out]  l_trajectory: total streamline distance so far
/// @param[in,out]  dxy1_vec: R-K first order delta step vector
/// @param[in,out]  dxy2_vec: R-K second order delta step vector
/// @param[in,out]  vec: current (x,y) coordinate vector at tip of streamline trajectory
/// @param[in,out]  prev_vec: previous (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  next_vec: next (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  n_steps: number of integration steps so far in streamline trajectory
/// @param[in,out]  idx: array index of pixel at current (x,y) position
/// @param[in,out]  prev_idx: array index of pixel at previous (x,y) position
/// @param[in,out]  trajectory_vec: streamline trajectory record
///                                  (2d array of compressed (x,y) vectors)
///
/// @retval bool:
///               @p true if stuck (step length less than @p INTEGRATION_HALT_THRESHOLD);
///               @p false otherwise aka step computed well
///
/// @ingroup integrationfns
///
static inline bool runge_kutta_step_record(float *dt, float  *dl, float *l_trajectory,
                                           float2 *dxy1_vec, float2 *dxy2_vec,
                                           float2 *vec, float2 *prev_vec,
                                           float2 *next_vec,
                                           uint *n_steps, uint *idx, uint *prev_idx,
                                           __global char2 *trajectory_vec)
{
    const float step_error = fast_length((*dxy2_vec-*dxy1_vec)/GRID_SCALE);

    *dl = fast_length(*dxy2_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
        *next_vec = *vec+*dxy1_vec;
        *dl = fast_length(*dxy1_vec);
    }
    *vec = *next_vec;
    *idx = get_array_idx(*next_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
        update_record_trajectory(*dl,l_trajectory,*vec,*prev_vec,n_steps,trajectory_vec);
//        printf("runge_kutta_step_record: stuck\n");
        return true;
    }
    *dt = select( fmin(DT_MAX,(ADJUSTED_MAX_ERROR*(*dt))/(step_error)), DT_MAX,
                 isequal(step_error,0.0f) );
    update_record_trajectory(*dl,l_trajectory,*vec,*prev_vec,n_steps,trajectory_vec);
    *prev_vec = *vec;
//    *prev_idx = select(*prev_idx,*idx,isnotequal(*idx,*prev_idx));
    *prev_idx = *idx;
    return false;
}
#endif

#ifdef KERNEL_INTEGRATE_TRAJECTORY
/// Compute a single Euler integration step of of a streamline.
/// Record this step in the global @p trajectory_vec (array of step vectors).
/// Also update the total streamline length @p l_trajectory and
///    increment the step counter @p n_steps.
///
/// Compiled if KERNEL_INTEGRATE_TRAJECTORY is defined.
///
/// @param[in,out]  dt: delta time step
/// @param[in,out]  dl: step distance
/// @param[in,out]  l_trajectory: total streamline distance so far
/// @param[in]      uv_vec: flow velocity vector interpolated to current position
/// @param[in,out]  vec: current (x,y) coordinate vector at tip of streamline trajectory
/// @param[in]      prev_vec: previous (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  n_steps: number of integration steps so far in streamline trajectory
/// @param[in,out]  trajectory_vec: streamline trajectory record
///                                  (2d array of compressed (x,y) vectors)
///
/// @retval bool:
///               @p true if stuck (step length less than @p INTEGRATION_HALT_THRESHOLD);
///               @p false otherwise aka step computed well
///
/// @ingroup integrationfns
///
static inline void euler_step_record(float *dt, float  *dl,
                                     float *l_trajectory, const float2 uv_vec,
                                     float2 *vec, const float2 prev_vec,
                                     uint *n_steps, __global char2 *trajectory_vec)
{
    const float2 sgnd_uv_vec = uv_vec*(float2)(DOWNUP_SIGN,DOWNUP_SIGN);
    const float dt_x = dt_to_nearest_edge((*vec)[0], sgnd_uv_vec[0]);
    const float dt_y = dt_to_nearest_edge((*vec)[1], sgnd_uv_vec[1]);

    *dt = minmag(dt_x,dt_y);
    *vec += sgnd_uv_vec*(*dt);
    *vec = approximate(*vec);
    *vec = (float2)( fmin(fmax((*vec)[0],-0.5f),NXF_MP5),
                     fmin(fmax((*vec)[1],-0.5f),NYF_MP5) );
    *dl = fast_length(*vec-prev_vec);
    update_record_trajectory(*dl,l_trajectory,*vec,prev_vec,n_steps,trajectory_vec);
 }
#endif

#ifdef KERNEL_INTEGRATE_FIELDS
///
/// Compute a single step of 2nd-order Runge-Kutta numerical integration of
///    a streamline given precomputed 1st and 2nd order step vectors.
/// If the step is deemed too small, return @p true = stuck;
///    otherwise return @p false = ok.
/// Record this step in the global @p slt_array and @p slc_array (streamline length total
///    and count total respectively) by updating both at the current pixel
///    (using @p mask_array to block where masked).
/// Also update the total streamline length @p l_trajectory and
///    increment the step counter @p n_steps.
/// In addition, push the current @p vec to the @p prev_vec, push the current @p idx to
///    the @p prev_idx, and update the @p idx of the current @p vec.
///
/// Compiled if KERNEL_INTEGRATE_FIELDS is defined.
///
/// @param[in,out]  dt: delta time step
/// @param[in,out]  dl: step distance
/// @param[in,out]  l_trajectory: total streamline distance so far
/// @param[in,out]  dxy1_vec: R-K first order delta step vector
/// @param[in,out]  dxy2_vec: R-K second order delta step vector
/// @param[in,out]  vec: current (x,y) coordinate vector at tip of streamline trajectory
/// @param[in,out]  prev_vec: previous (x,y) coordinate vector on streamline trajectory
/// @param[in]      next_vec: next (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  n_steps: number of integration steps so far in streamline trajectory
/// @param[in,out]  idx: array index of pixel at current (x,y) position
/// @param[in]  mask_array: grid pixel mask (padded),
///                         with @p true = masked, @p false = good
/// @param[in,out]  mapping_array: flag grid recording status of each pixel (padded)
/// @param[in,out] slc_array: grid recording accumulated count of streamline integration
///                           steps across each pixel (padded)
/// @param[in,out] slt_array: grid recording accumulated count of streamline segment
///                           lengths crossing each pixel (padded)
///
/// @retval bool:
///               @p true if stuck (step length less than @p INTEGRATION_HALT_THRESHOLD);
///               @p false otherwise aka step computed well
///
/// @ingroup integrationfns
///
static inline bool runge_kutta_step_write_sl_data(
                float *dt, float  *dl, float *l_trajectory,
                float2 *dxy1_vec, float2 *dxy2_vec,
                float2 *vec, float2 *prev_vec, const float2 next_vec,
                uint *n_steps, uint *idx,
                __global const bool *mask_array, __global uint *mapping_array,
                __global uint *slt_array, __global uint *slc_array)
{
    const float step_error = fast_length((*dxy2_vec-*dxy1_vec)/GRID_SCALE);

    *dl = fast_length(*dxy2_vec);
    *vec = next_vec;
    if ((*dl<(INTEGRATION_HALT_THRESHOLD)) ) {
        update_trajectory_write_sl_data(*dl,l_trajectory,*vec,*prev_vec,n_steps,
                                        idx, mask_array, slt_array, slc_array);
        return true;
    }
    *dt = select( fmin(DT_MAX,(ADJUSTED_MAX_ERROR*(*dt))/(step_error)), DT_MAX,
                 isequal(step_error,0.0f) );
    update_trajectory_write_sl_data(*dl,l_trajectory,*vec,*prev_vec,n_steps,
                                    idx, mask_array, slt_array, slc_array);
    *prev_vec = *vec;
    return false;
}
#endif

#ifdef KERNEL_INTEGRATE_FIELDS
///
/// Compute a single Euler integration step of of a streamline.
/// Record this step in the global @p trajectory_vec (array of step vectors).
/// Also update the total streamline length @p l_trajectory and
///    increment the step counter @p n_steps.
///
/// Compiled if KERNEL_INTEGRATE_FIELDS is defined.
///
/// @param[in,out]  dt: delta time step
/// @param[in,out]  dl: step distance
/// @param[in,out]  l_trajectory: total streamline distance so far
/// @param[in]      uv_vec: flow velocity vector interpolated to current position
/// @param[in,out]  vec: current (x,y) coordinate vector at tip of streamline trajectory
/// @param[in]      prev_vec: previous (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  n_steps: number of integration steps so far in streamline trajectory
/// @param[in,out]  idx: array index of pixel at current (x,y) position
/// @param[in]      mask_array: grid pixel mask (padded),
///                         with @p true = masked, @p false = good
/// @param[in,out]  slc_array: grid recording accumulated count of streamline integration
///                           steps across each pixel (padded)
/// @param[in,out]  slt_array: grid recording accumulated count of streamline segment
///                           lengths crossing each pixel (padded)
///
/// @retval bool:
///               @p true if stuck (step length less than @p INTEGRATION_HALT_THRESHOLD);
///               @p false otherwise aka step computed well
///
/// @ingroup integrationfns
///
static inline void euler_step_write_sl_data(float *dt, float *dl,
                                            float *l_trajectory,
                                            const float2 uv_vec, float2 *vec,
                                            const float2 prev_vec,
                                            uint *n_steps,
                                            uint *idx,
                                            __global const bool *mask_array,
                                            __global uint *slt_array,
                                            __global uint *slc_array)
{
    __private float2 sgnd_uv_vec;
    __private float dt_x, dt_y;

    sgnd_uv_vec = uv_vec*(float2)(DOWNUP_SIGN,DOWNUP_SIGN);
    dt_x = dt_to_nearest_edge((*vec)[0], sgnd_uv_vec[0]);
    dt_y = dt_to_nearest_edge((*vec)[1], sgnd_uv_vec[1]);
    *dt = minmag(dt_x,dt_y);
    *vec += sgnd_uv_vec*(*dt);
    *vec = approximate(*vec);
    *vec = (float2)( fmin(fmax((*vec)[0],-0.5f),NXF_MP5),
                     fmin(fmax((*vec)[1],-0.5f),NYF_MP5) );
    *dl = fast_length(*vec-prev_vec);
    update_trajectory_write_sl_data(*dl,l_trajectory,*vec,prev_vec,n_steps,
                                    idx, mask_array, slt_array, slc_array);
 }
#endif

#ifdef KERNEL_CONNECT_CHANNELS
///
/// Compute a single step of 2nd-order Runge-Kutta numerical integration of
///    a streamline given precomputed 1st and 2nd order step vectors.
/// If the step is deemed too small, return @p true = stuck;
///    otherwise return @p false = ok.
/// Record this step in the private @p trajectory_vec (array of step vectors).
/// Also update the total streamline length @p l_trajectory and
///    increment the step counter @p n_steps.
/// In addition, push the current @p vec to the @p prev_vec, push the current @p idx to
///    the @p prev_idx, and update the @p idx of the current @p vec.
///
/// Compiled if KERNEL_CONNECT_CHANNELS is defined.
///
/// @param[in,out]  dt: delta time step
/// @param[in,out]  dl: step distance
/// @param[in,out]  l_trajectory: total streamline distance so far
/// @param[in,out]  dxy1_vec: R-K first order delta step vector
/// @param[in,out]  dxy2_vec: R-K second order delta step vector
/// @param[in,out]  vec: current (x,y) coordinate vector at tip of streamline trajectory
/// @param[in,out]  prev_vec: previous (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  next_vec: next (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  n_steps: number of integration steps so far in streamline trajectory
/// @param[in,out]  idx: array index of pixel at current (x,y) position
/// @param[in,out]  prev_idx: array index of pixel at previous (x,y) position
/// @param[in,out]  trajectory_vec: streamline trajectory record
///                                  (2d array of compressed (x,y) vectors)
///
/// @retval bool:
///               @p true if stuck (step length less than @p INTEGRATION_HALT_THRESHOLD);
///               @p false otherwise aka step computed well
///
/// @ingroup integrationfns
///
static inline bool connect_runge_kutta_step_record(float *dt, float  *dl,
                                                   float *l_trajectory,
                                                   float2 *dxy1_vec, float2 *dxy2_vec,
                                                   float2 *vec, float2 *prev_vec,
                                                   float2 *next_vec,
                                                   uint *n_steps,
                                                   uint *idx, uint *prev_idx,
                                                   __private char2 *trajectory_vec)
{
    const float step_error = fast_length((*dxy2_vec-*dxy1_vec)/GRID_SCALE);

    *dl = fast_length(*dxy2_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
        *next_vec = *vec+*dxy1_vec;
        *dl = fast_length(*dxy1_vec);
    }
    *vec = *next_vec;
    *idx = get_array_idx(*next_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
        update_record_private_trajectory(*dl,l_trajectory,*vec,*prev_vec,n_steps,
                                         trajectory_vec);
        return true;
    }
    *dt = select( fmin(DT_MAX,(ADJUSTED_MAX_ERROR*(*dt))/(step_error)), DT_MAX,
                 isequal(step_error,0.0f) );
    update_record_private_trajectory(*dl,l_trajectory,*vec,*prev_vec,n_steps,
                                     trajectory_vec);
    *prev_vec = *vec;
    return false;
}
#endif

#ifdef KERNEL_MAP_CHANNEL_HEADS
///
/// Compute a single step of 2nd-order Runge-Kutta numerical integration of
///    a streamline given precomputed 1st and 2nd order step vectors.
/// Increment the step counter @p n_steps and update the @p idx of the new @p vec.
///
/// Compiled if KERNEL_INTEGRATE_TRAJECTORY is defined.
///
/// @param[in,out]  dt: delta time step
/// @param[in,out]  dl: step distance
/// @param[in,out]  dxy1_vec: R-K first order delta step vector
/// @param[in,out]  dxy2_vec: R-K second order delta step vector
/// @param[in,out]  vec: current (x,y) coordinate vector at tip of streamline trajectory
/// @param[in,out]  next_vec: next (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  n_steps: number of integration steps so far in streamline trajectory
/// @param[in,out]  idx: array index of pixel at current (x,y) position
///
/// @retval bool:
///               @p true if stuck (step length less than @p INTEGRATION_HALT_THRESHOLD);
///               @p false otherwise aka step computed well
///
/// @ingroup integrationfns
///
static inline void channelheads_runge_kutta_step(float *dt, float *dl,
                                                 float2 *dxy1_vec, float2 *dxy2_vec,
                                                 float2 *vec, float2 *next_vec,
                                                 uint *n_steps, uint *idx)
{
    const float step_error = fast_length((*dxy2_vec-*dxy1_vec)/GRID_SCALE);

    *dl = fast_length(*dxy2_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
        *next_vec = *vec+*dxy1_vec;
        *dl = fast_length(*dxy1_vec);
    }
    *vec = *next_vec;
    *idx = get_array_idx(*next_vec);
    *n_steps += 1u;
    *dt = select( fmin(DT_MAX,(ADJUSTED_MAX_ERROR*(*dt))/(step_error)), DT_MAX,
                 isequal(step_error,0.0f) );
    return;
}
#endif

#if defined(KERNEL_COUNT_DOWNCHANNELS) || defined(KERNEL_HILLSLOPES)
///
/// Compute a single step of 2nd-order Runge-Kutta numerical integration of
///    a streamline given precomputed 1st and 2nd order step vectors.
/// If the step is deemed too small, return @p true = stuck;
///    otherwise return @p false = ok.
/// In addition, flag the @p mapping_array pixel as @p IS_STUCK.
/// Update the @p idx of the current (new) @p vec.
///
/// Compiled if KERNEL_COUNT_DOWNCHANNELS or KERNEL_HILLSLOPES is defined.
///
/// @param[in,out]  dt: delta time step
/// @param[in,out]  dl: step distance
/// @param[in,out]  dxy1_vec: R-K first order delta step vector
/// @param[in,out]  dxy2_vec: R-K second order delta step vector
/// @param[in,out]  vec: current (x,y) coordinate vector at tip of streamline trajectory
/// @param[in,out]  next_vec: next (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  idx: array index of pixel at current (x,y) position
/// @param[in,out]  mapping_array: flag grid recording status of each pixel (padded)
///
/// @retval bool:
///               @p true if stuck (step length less than @p INTEGRATION_HALT_THRESHOLD);
///               @p false otherwise aka step computed well
///
/// @ingroup integrationfns
///
static inline bool countlink_runge_kutta_step(float *dt, float *dl,
                                              float2 *dxy1_vec, float2 *dxy2_vec,
                                              float2 *vec, float2 *next_vec, uint *idx,
                                              __global uint  *mapping_array)
{
    const float step_error = fast_length((*dxy2_vec-*dxy1_vec)/GRID_SCALE);

    *dl = fast_length(*dxy2_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
        *next_vec = *vec+*dxy1_vec;
        *dl = fast_length(*dxy1_vec);
    }
    *vec = *next_vec;
    *idx = get_array_idx(*next_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
#ifdef DEBUG
        printf("Count-link @ %g,%g: stuck\n",(*vec)[0],(*vec)[1]);
#endif
//        atomic_or(&mapping_array[*idx],IS_STUCK);
        return true;
    }
    *dt = select( fmin(DT_MAX,(ADJUSTED_MAX_ERROR*(*dt))/(step_error)), DT_MAX,
                 isequal(step_error,0.0f) );
    return false;
}
#endif

#if defined(KERNEL_SEGMENT_HILLSLOPES) || defined(KERNEL_SUBSEGMENT_FLANKS) \
   || defined(KERNEL_FIX_RIGHT_FLANKS) || defined(KERNEL_FIX_LEFT_FLANKS)
///
/// Compute a single step of 2nd-order Runge-Kutta numerical integration of
///    a streamline given precomputed 1st and 2nd order step vectors.
/// If the step is deemed too small, return @p true = stuck;
///    otherwise return @p false = ok.
/// Increment the step counter @p n_steps and update the @p idx of the new (next) @p vec.
///
/// Compiled if KERNEL_SEGMENT_HILLSLOPES or KERNEL_SUBSEGMENT_FLANKS is defined.
///
/// @param[in,out]  dt: delta time step
/// @param[in,out]  dl: step distance
/// @param[in,out]  dxy1_vec: R-K first order delta step vector
/// @param[in,out]  dxy2_vec: R-K second order delta step vector
/// @param[in,out]  vec: current (x,y) coordinate vector at tip of streamline trajectory
/// @param[in,out]  next_vec: next (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  n_steps: number of integration steps so far in streamline trajectory
/// @param[in,out]  idx: array index of pixel at current (x,y) position
///
/// @retval bool:
///               @p true if stuck (step length less than @p INTEGRATION_HALT_THRESHOLD);
///               @p false otherwise aka step computed well
///
/// @ingroup integrationfns
///
static inline bool segment_runge_kutta_step(float *dt, float *dl,
                                            float2 *dxy1_vec, float2 *dxy2_vec,
                                            float2 *vec, float2 *next_vec,
                                            uint *n_steps, uint *idx)
{
    const float step_error = fast_length((*dxy2_vec-*dxy1_vec)/GRID_SCALE);

    *dl = fast_length(*dxy2_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
        *next_vec = *vec+*dxy1_vec;
        *dl = fast_length(*dxy1_vec);
    }
    *vec = *next_vec;
    *idx = get_array_idx(*next_vec);
    *n_steps += 1u;
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
#ifdef DEBUG
        printf("Segment @ %g,%g: stuck\n",(*vec)[0],(*vec)[1]);
#endif
        return true;
    }
    *dt = select( fmin(DT_MAX,(ADJUSTED_MAX_ERROR*(*dt))/(step_error)), DT_MAX,
                 isequal(step_error,0.0f) );
    return false;
}
#endif

#ifdef KERNEL_HILLSLOPE_LENGTHS
///
/// Compute a single step of 2nd-order Runge-Kutta numerical integration of
///    a streamline given precomputed 1st and 2nd order step vectors.
/// If the step is deemed too small, return @p true = stuck;
///    otherwise return @p false = ok.
/// Also update the total streamline length @p l_trajectory and
///    increment the step counter @p n_steps.
/// In addition, push the current @p vec to the @p prev_vec and
///    update the @p idx of the current @p vec.
///
/// Compiled if KERNEL_HILLSLOPE_LENGTHS is defined.
///
/// @param[in,out]  dt: delta time step
/// @param[in,out]  dl: step distance
/// @param[in,out]  l_trajectory: total streamline distance so far
/// @param[in,out]  dxy1_vec: R-K first order delta step vector
/// @param[in,out]  dxy2_vec: R-K second order delta step vector
/// @param[in,out]  vec: current (x,y) coordinate vector at tip of streamline trajectory
/// @param[in,out]  prev_vec: previous (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  next_vec: next (x,y) coordinate vector on streamline trajectory
/// @param[in,out]  n_steps: number of integration steps so far in streamline trajectory
/// @param[in,out]  idx: array index of pixel at current (x,y) position
///
/// @retval bool:
///               @p true if stuck (step length less than @p INTEGRATION_HALT_THRESHOLD);
///               @p false otherwise aka step computed well
///
/// @ingroup integrationfns
///
static inline bool lengths_runge_kutta_step(float *dt, float  *dl, float *l_trajectory,
                                            float2 *dxy1_vec, float2 *dxy2_vec,
                                            float2 *vec, float2 *prev_vec,
                                            float2 *next_vec,
                                            uint *n_steps, uint *idx)
{
    const float step_error = fast_length((*dxy2_vec-*dxy1_vec)/GRID_SCALE);

    *dl = fast_length(*dxy2_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
        *next_vec = *vec+*dxy1_vec;
        *dl = fast_length(*dxy1_vec);
    }
    *vec = *next_vec;
    *idx = get_array_idx(*next_vec);
    if (*dl<(INTEGRATION_HALT_THRESHOLD)) {
        update_trajectory(*dl,l_trajectory,n_steps);
        return true;
    }
    *dt = select( fmin(DT_MAX,(ADJUSTED_MAX_ERROR*(*dt))/(step_error)), DT_MAX,
                 isequal(step_error,0.0f) );
    update_trajectory(*dl,l_trajectory,n_steps);
    *prev_vec = *vec;
    return false;
}
#endif