/* Wrapper. */
void oskar_convert_ludwig3_to_theta_phi_components(oskar_Mem* vec,
        int offset, int stride, int num_points, const oskar_Mem* phi,
        int* status)
{
    int type, location;

    /* Check if safe to proceed. */
    if (*status) return;

    /* Check that the vector component data is in matrix form. */
    if (!oskar_mem_is_matrix(vec))
    {
        *status = OSKAR_ERR_TYPE_MISMATCH;
        return;
    }

    /* Get data type and location. */
    type = oskar_mem_type(phi);
    location = oskar_mem_location(phi);

    /* Convert vector representation from Ludwig-3 to spherical. */
    if (type == OSKAR_SINGLE)
    {
        float2 *h_theta, *v_phi;
        const float *phi_;
        h_theta = oskar_mem_float2(vec, status) + offset;
        v_phi = oskar_mem_float2(vec, status) + offset + 1;
        phi_ = oskar_mem_float_const(phi, status);

        if (location == OSKAR_GPU)
        {
#ifdef OSKAR_HAVE_CUDA
            oskar_convert_ludwig3_to_theta_phi_components_cuda_f(num_points,
                    h_theta, v_phi, phi_, stride);
            oskar_device_check_error(status);
#else
            *status = OSKAR_ERR_CUDA_NOT_AVAILABLE;
#endif
        }
        else if (location == OSKAR_CPU)
        {
            oskar_convert_ludwig3_to_theta_phi_components_f(num_points,
                    h_theta, v_phi, phi_, stride);
        }
    }
    else if (type == OSKAR_DOUBLE)
    {
        double2 *h_theta, *v_phi;
        const double *phi_;
        h_theta = oskar_mem_double2(vec, status) + offset;
        v_phi = oskar_mem_double2(vec, status) + offset + 1;
        phi_ = oskar_mem_double_const(phi, status);

        if (location == OSKAR_GPU)
        {
#ifdef OSKAR_HAVE_CUDA
            oskar_convert_ludwig3_to_theta_phi_components_cuda_d(num_points,
                    h_theta, v_phi, phi_, stride);
            oskar_device_check_error(status);
#else
            *status = OSKAR_ERR_CUDA_NOT_AVAILABLE;
#endif
        }
        else if (location == OSKAR_CPU)
        {
            oskar_convert_ludwig3_to_theta_phi_components_d(num_points,
                    h_theta, v_phi, phi_, stride);
        }
    }
    else
        *status = OSKAR_ERR_BAD_DATA_TYPE;
}
示例#2
0
void oskar_evaluate_station_beam(oskar_Mem* beam_pattern, int num_points,
        int coord_type, oskar_Mem* x, oskar_Mem* y, oskar_Mem* z,
        double norm_ra_rad, double norm_dec_rad, const oskar_Station* station,
        oskar_StationWork* work, int time_index, double frequency_hz,
        double GAST, int* status)
{
    int normalise_final_beam;
    oskar_Mem* out;

    /* Check if safe to proceed. */
    if (*status) return;

    /* Set default output beam array. */
    out = beam_pattern;

    /* Check that the arrays have enough space to add an extra source at the
     * end (for normalisation). We don't want to reallocate here, since that
     * will be slow to do each time: must simply ensure that we pass input
     * arrays that are large enough.
     * The normalisation doesn't need to happen if the station has an
     * isotropic beam. */
    normalise_final_beam = oskar_station_normalise_final_beam(station) &&
            (oskar_station_type(station) != OSKAR_STATION_TYPE_ISOTROPIC);
    if (normalise_final_beam)
    {
        double c_x = 0.0, c_y = 0.0, c_z = 1.0;

        /* Increment number of points. */
        num_points++;

        /* Check the input arrays are big enough to hold the new source. */
        if ((int)oskar_mem_length(x) < num_points ||
                (int)oskar_mem_length(y) < num_points ||
                (int)oskar_mem_length(z) < num_points)
        {
            *status = OSKAR_ERR_DIMENSION_MISMATCH;
            return;
        }

        /* Set output beam array to work buffer. */
        out = oskar_station_work_normalised_beam(work, beam_pattern, status);

        /* Get the beam direction in the appropriate coordinate system. */
        /* (Direction cosines are already set to the interferometer phase
         * centre for relative directions.) */
        if (coord_type == OSKAR_ENU_DIRECTIONS)
        {
            double t_x, t_y, t_z, ha0;
            ha0 = (GAST + oskar_station_lon_rad(station)) - norm_ra_rad;
            oskar_convert_relative_directions_to_enu_directions_d(
                    &t_x, &t_y, &t_z, 1, &c_x, &c_y, &c_z, ha0, norm_dec_rad,
                    oskar_station_lat_rad(station));
            c_x = t_x;
            c_y = t_y;
            c_z = t_z;
        }

        /* Add the extra normalisation source to the end of the arrays. */
        oskar_mem_set_element_real(x, num_points-1, c_x, status);
        oskar_mem_set_element_real(y, num_points-1, c_y, status);
        oskar_mem_set_element_real(z, num_points-1, c_z, status);
    }

    /* Evaluate the station beam for the given directions. */
    if (coord_type == OSKAR_ENU_DIRECTIONS)
    {
        evaluate_station_beam_enu_directions(out, num_points, x, y, z,
                station, work, time_index, frequency_hz, GAST, status);
    }
    else if (coord_type == OSKAR_RELATIVE_DIRECTIONS)
    {
        evaluate_station_beam_relative_directions(out, num_points, x, y, z,
                station, work, time_index, frequency_hz, GAST, status);
    }
    else
    {
        *status = OSKAR_ERR_INVALID_ARGUMENT;
    }

    /* Scale beam pattern by value of the last source if required. */
    if (normalise_final_beam)
    {
        double amp = 0.0;

        /* Get the last element of the vector and convert to amplitude. */
        if (oskar_mem_is_matrix(out))
        {
            double4c val;
            val = oskar_mem_get_element_matrix(out, num_points-1, status);

            /*
             * Scale by square root of "Stokes I" autocorrelation:
             * sqrt(0.5 * [sum of resultant diagonal]).
             *
             * We have
             * [ Xa  Xb ] [ Xa*  Xc* ] = [ Xa Xa* + Xb Xb*    (don't care)   ]
             * [ Xc  Xd ] [ Xb*  Xd* ]   [  (don't care)     Xc Xc* + Xd Xd* ]
             *
             * Stokes I is completely real, so need only evaluate the real
             * part of all the multiplies. Because of the conjugate terms,
             * these become re*re + im*im.
             *
             * Need the square root because we only want the normalised value
             * for the beam itself (in isolation), not its actual
             * autocorrelation!
             */
            amp = val.a.x * val.a.x + val.a.y * val.a.y +
                    val.b.x * val.b.x + val.b.y * val.b.y +
                    val.c.x * val.c.x + val.c.y * val.c.y +
                    val.d.x * val.d.x + val.d.y * val.d.y;
            amp = sqrt(0.5 * amp);
        }
        else
        {
            double2 val;
            val = oskar_mem_get_element_complex(out, num_points-1, status);

            /* Scale by voltage. */
            amp = sqrt(val.x * val.x + val.y * val.y);
        }

        /* Scale beam array by normalisation value. */
        oskar_mem_scale_real(out, 1.0/amp, status);

        /* Copy output beam data. */
        oskar_mem_copy_contents(beam_pattern, out, 0, 0, num_points-1, status);
    }
}
示例#3
0
void oskar_element_evaluate(
        const oskar_Element* model,
        double orientation_x,
        double orientation_y,
        int offset_points,
        int num_points,
        const oskar_Mem* x,
        const oskar_Mem* y,
        const oskar_Mem* z,
        double frequency_hz,
        oskar_Mem* theta,
        oskar_Mem* phi,
        int offset_out,
        oskar_Mem* output,
        int* status)
{
    double dipole_length_m;
    if (*status) return;
    if (!oskar_mem_is_complex(output))
    {
        *status = OSKAR_ERR_BAD_DATA_TYPE;
        return;
    }
    oskar_mem_ensure(output, offset_out + num_points, status);
    oskar_mem_ensure(theta, num_points, status);
    oskar_mem_ensure(phi, num_points, status);

    /* Get the element model properties. */
    const int element_type = model->element_type;
    const int taper_type   = model->taper_type;
    const int id = oskar_find_closest_match_d(frequency_hz,
            oskar_element_num_freq(model),
            oskar_element_freqs_hz_const(model));
    dipole_length_m = model->dipole_length;
    if (model->dipole_length_units == OSKAR_WAVELENGTHS)
        dipole_length_m *= (C_0 / frequency_hz);

    /* Compute modified theta and phi coordinates for dipole X. */
    oskar_convert_enu_directions_to_theta_phi(offset_points, num_points,
            x, y, z, (M_PI/2) - orientation_x, theta, phi, status);

    /* Check if element type is isotropic. */
    if (element_type == OSKAR_ELEMENT_TYPE_ISOTROPIC)
        oskar_mem_set_value_real(output, 1.0, offset_out, num_points, status);

    /* Evaluate polarised response if output array is matrix type. */
    if (oskar_mem_is_matrix(output))
    {
        const int offset_out_real = offset_out * 8;
        const int offset_out_cplx = offset_out * 4;
        if (oskar_element_has_x_spherical_wave_data(model, id))
            oskar_evaluate_spherical_wave_sum(num_points, theta, phi,
                    model->x_lmax[id], model->x_te[id], model->x_tm[id],
                    4, offset_out_cplx + 0, output, status);
        else if (oskar_element_has_x_spline_data(model, id))
        {
            oskar_splines_evaluate(model->x_h_re[id], num_points, theta, phi,
                    8, offset_out_real + 0, output, status);
            oskar_splines_evaluate(model->x_h_im[id], num_points, theta, phi,
                    8, offset_out_real + 1, output, status);
            oskar_splines_evaluate(model->x_v_re[id], num_points, theta, phi,
                    8, offset_out_real + 2, output, status);
            oskar_splines_evaluate(model->x_v_im[id], num_points, theta, phi,
                    8, offset_out_real + 3, output, status);
            oskar_convert_ludwig3_to_theta_phi_components(num_points, phi,
                    4, offset_out_cplx + 0, output, status);
        }
        else if (element_type == OSKAR_ELEMENT_TYPE_DIPOLE)
            oskar_evaluate_dipole_pattern(num_points, theta, phi,
                    frequency_hz, dipole_length_m,
                    4, offset_out_cplx + 0, output, status);
        else if (element_type == OSKAR_ELEMENT_TYPE_GEOMETRIC_DIPOLE)
            oskar_evaluate_geometric_dipole_pattern(num_points, theta, phi,
                    4, offset_out_cplx + 0, output, status);

        /* Compute modified theta and phi coordinates for dipole Y. */
        oskar_convert_enu_directions_to_theta_phi(offset_points, num_points,
                x, y, z, (M_PI/2) - orientation_y, theta, phi, status);

        if (oskar_element_has_y_spherical_wave_data(model, id))
            oskar_evaluate_spherical_wave_sum(num_points, theta, phi,
                    model->y_lmax[id], model->y_te[id], model->y_tm[id],
                    4, offset_out_cplx + 2, output, status);
        else if (oskar_element_has_y_spline_data(model, id))
        {
            oskar_splines_evaluate(model->y_h_re[id], num_points, theta, phi,
                    8, offset_out_real + 4, output, status);
            oskar_splines_evaluate(model->y_h_im[id], num_points, theta, phi,
                    8, offset_out_real + 5, output, status);
            oskar_splines_evaluate(model->y_v_re[id], num_points, theta, phi,
                    8, offset_out_real + 6, output, status);
            oskar_splines_evaluate(model->y_v_im[id], num_points, theta, phi,
                    8, offset_out_real + 7, output, status);
            oskar_convert_ludwig3_to_theta_phi_components(num_points, phi,
                    4, offset_out_cplx + 2, output, status);
        }
        else if (element_type == OSKAR_ELEMENT_TYPE_DIPOLE)
            oskar_evaluate_dipole_pattern(num_points, theta, phi,
                    frequency_hz, dipole_length_m,
                    4, offset_out_cplx + 2, output, status);
        else if (element_type == OSKAR_ELEMENT_TYPE_GEOMETRIC_DIPOLE)
            oskar_evaluate_geometric_dipole_pattern(num_points, theta, phi,
                    4, offset_out_cplx + 2, output, status);
    }
    else /* Scalar response. */
    {
        const int offset_out_real = offset_out * 2;
        if (oskar_element_has_scalar_spline_data(model, id))
        {
            oskar_splines_evaluate(model->scalar_re[id], num_points, theta, phi,
                    2, offset_out_real + 0, output, status);
            oskar_splines_evaluate(model->scalar_im[id], num_points, theta, phi,
                    2, offset_out_real + 1, output, status);
        }
        else if (element_type == OSKAR_ELEMENT_TYPE_DIPOLE)
            oskar_evaluate_dipole_pattern(num_points, theta, phi,
                    frequency_hz, dipole_length_m,
                    1, offset_out, output, status);
        else if (element_type == OSKAR_ELEMENT_TYPE_GEOMETRIC_DIPOLE)
            oskar_evaluate_geometric_dipole_pattern(num_points, theta, phi,
                    1, offset_out, output, status);
    }

    /* Apply element tapering, if specified. */
    if (taper_type == OSKAR_ELEMENT_TAPER_COSINE)
        oskar_apply_element_taper_cosine(num_points,
                model->cosine_power, theta, offset_out, output, status);
    else if (taper_type == OSKAR_ELEMENT_TAPER_GAUSSIAN)
        oskar_apply_element_taper_gaussian(num_points,
                model->gaussian_fwhm_rad, theta, offset_out, output, status);
}
void oskar_mem_evaluate_relative_error(const oskar_Mem* val_approx,
        const oskar_Mem* val_accurate, double* min_rel_error,
        double* max_rel_error, double* avg_rel_error, double* std_rel_error,
        int* status)
{
    int prec_approx, prec_accurate;
    size_t i, n;
    const oskar_Mem *app_ptr, *acc_ptr;
    oskar_Mem *approx_temp = 0, *accurate_temp = 0;
    double old_m = 0.0, new_m = 0.0, old_s = 0.0, new_s = 0.0;

    /* Check if safe to proceed. */
    if (*status) return;

    /* Initialise outputs. */
    if (max_rel_error) *max_rel_error = -DBL_MAX;
    if (min_rel_error) *min_rel_error = DBL_MAX;
    if (avg_rel_error) *avg_rel_error = DBL_MAX;
    if (std_rel_error) *std_rel_error = DBL_MAX;

    /* Type and dimension check. */
    if (oskar_mem_is_matrix(val_approx) && !oskar_mem_is_matrix(val_accurate))
    {
        *status = OSKAR_ERR_TYPE_MISMATCH;
        return;
    }
    if (oskar_mem_is_complex(val_approx) && !oskar_mem_is_complex(val_accurate))
    {
        *status = OSKAR_ERR_TYPE_MISMATCH;
        return;
    }

    /* Get and check base types. */
    prec_approx = oskar_mem_precision(val_approx);
    prec_accurate = oskar_mem_precision(val_accurate);
    if (prec_approx != OSKAR_SINGLE && prec_approx != OSKAR_DOUBLE)
    {
        *status = OSKAR_ERR_BAD_DATA_TYPE;
        return;
    }
    if (prec_accurate != OSKAR_SINGLE && prec_accurate != OSKAR_DOUBLE)
    {
        *status = OSKAR_ERR_BAD_DATA_TYPE;
        return;
    }

    /* Get number of elements to check. */
    n = oskar_mem_length(val_approx) < oskar_mem_length(val_accurate) ?
            oskar_mem_length(val_approx) : oskar_mem_length(val_accurate);
    if (oskar_mem_is_matrix(val_approx)) n *= 4;

    /* Copy input data to temporary CPU arrays if required. */
    app_ptr = val_approx;
    acc_ptr = val_accurate;
    if (oskar_mem_location(val_approx) != OSKAR_CPU)
    {
        approx_temp = oskar_mem_create_copy(val_approx, OSKAR_CPU,
                status);
        if (*status)
        {
            oskar_mem_free(approx_temp, status);
            return;
        }
        app_ptr = approx_temp;
    }
    if (oskar_mem_location(val_accurate) != OSKAR_CPU)
    {
        accurate_temp = oskar_mem_create_copy(val_accurate, OSKAR_CPU,
                status);
        if (*status)
        {
            oskar_mem_free(accurate_temp, status);
            return;
        }
        acc_ptr = accurate_temp;
    }

    /* Check numbers are the same, to appropriate precision. */
    if (prec_approx == OSKAR_SINGLE && prec_accurate == OSKAR_SINGLE)
    {
        const float *approx, *accurate;
        approx = oskar_mem_float_const(app_ptr, status);
        accurate = oskar_mem_float_const(acc_ptr, status);
        CHECK_ELEMENTS(1e-5)
    }
    else if (prec_approx == OSKAR_DOUBLE && prec_accurate == OSKAR_SINGLE)
void oskar_auto_correlate(oskar_Mem* vis, int n_sources, const oskar_Jones* J,
        const oskar_Sky* sky, int* status)
{
    int jones_type, base_type, location, matrix_type, n_stations;

    /* Check if safe to proceed. */
    if (*status) return;

    /* Get the data dimensions. */
    n_stations = oskar_jones_num_stations(J);

    /* Check data locations. */
    location = oskar_sky_mem_location(sky);
    if (oskar_jones_mem_location(J) != location ||
            oskar_mem_location(vis) != location)
    {
        *status = OSKAR_ERR_LOCATION_MISMATCH;
        return;
    }

    /* Check for consistent data types. */
    jones_type = oskar_jones_type(J);
    base_type = oskar_sky_precision(sky);
    matrix_type = oskar_type_is_matrix(jones_type) &&
            oskar_mem_is_matrix(vis);
    if (oskar_mem_precision(vis) != base_type ||
            oskar_type_precision(jones_type) != base_type)
    {
        *status = OSKAR_ERR_TYPE_MISMATCH;
        return;
    }
    if (oskar_mem_type(vis) != jones_type)
    {
        *status = OSKAR_ERR_TYPE_MISMATCH;
        return;
    }

    /* If neither single or double precision, return error. */
    if (base_type != OSKAR_SINGLE && base_type != OSKAR_DOUBLE)
    {
        *status = OSKAR_ERR_BAD_DATA_TYPE;
        return;
    }

    /* Check the input dimensions. */
    if (oskar_jones_num_sources(J) < n_sources)
    {
        *status = OSKAR_ERR_DIMENSION_MISMATCH;
        return;
    }

    /* Select kernel. */
    if (base_type == OSKAR_DOUBLE)
    {
        const double *I_, *Q_, *U_, *V_;
        I_ = oskar_mem_double_const(oskar_sky_I_const(sky), status);
        Q_ = oskar_mem_double_const(oskar_sky_Q_const(sky), status);
        U_ = oskar_mem_double_const(oskar_sky_U_const(sky), status);
        V_ = oskar_mem_double_const(oskar_sky_V_const(sky), status);

        if (matrix_type)
        {
            double4c *vis_;
            const double4c *J_;
            vis_ = oskar_mem_double4c(vis, status);
            J_   = oskar_jones_double4c_const(J, status);

            if (location == OSKAR_GPU)
            {
#ifdef OSKAR_HAVE_CUDA
                oskar_auto_correlate_cuda_d(n_sources, n_stations,
                        J_, I_, Q_, U_, V_, vis_);
                oskar_device_check_error(status);
#else
                *status = OSKAR_ERR_CUDA_NOT_AVAILABLE;
#endif
            }
            else /* CPU */
            {
                oskar_auto_correlate_omp_d(n_sources, n_stations,
                        J_, I_, Q_, U_, V_, vis_);
            }
        }
        else /* Scalar version. */
        {
            double2 *vis_;
            const double2 *J_;
            vis_ = oskar_mem_double2(vis, status);
            J_   = oskar_jones_double2_const(J, status);

            if (location == OSKAR_GPU)
            {
#ifdef OSKAR_HAVE_CUDA
                oskar_auto_correlate_scalar_cuda_d(n_sources, n_stations,
                        J_, I_, vis_);
                oskar_device_check_error(status);
#else
                *status = OSKAR_ERR_CUDA_NOT_AVAILABLE;
#endif
            }
            else /* CPU */
            {
                oskar_auto_correlate_scalar_omp_d(n_sources, n_stations,
                        J_, I_, vis_);
            }
        }
    }
    else /* Single precision. */
    {
        const float *I_, *Q_, *U_, *V_;
        I_ = oskar_mem_float_const(oskar_sky_I_const(sky), status);
        Q_ = oskar_mem_float_const(oskar_sky_Q_const(sky), status);
        U_ = oskar_mem_float_const(oskar_sky_U_const(sky), status);
        V_ = oskar_mem_float_const(oskar_sky_V_const(sky), status);

        if (matrix_type)
        {
            float4c *vis_;
            const float4c *J_;
            vis_ = oskar_mem_float4c(vis, status);
            J_   = oskar_jones_float4c_const(J, status);

            if (location == OSKAR_GPU)
            {
#ifdef OSKAR_HAVE_CUDA
                oskar_auto_correlate_cuda_f(n_sources, n_stations,
                        J_, I_, Q_, U_, V_, vis_);
                oskar_device_check_error(status);
#else
                *status = OSKAR_ERR_CUDA_NOT_AVAILABLE;
#endif
            }
            else /* CPU */
            {
                oskar_auto_correlate_omp_f(n_sources, n_stations,
                        J_, I_, Q_, U_, V_, vis_);
            }
        }
        else /* Scalar version. */
        {
            float2 *vis_;
            const float2 *J_;
            vis_ = oskar_mem_float2(vis, status);
            J_   = oskar_jones_float2_const(J, status);

            if (location == OSKAR_GPU)
            {
#ifdef OSKAR_HAVE_CUDA
                oskar_auto_correlate_scalar_cuda_f(n_sources, n_stations,
                        J_, I_, vis_);
                oskar_device_check_error(status);
#else
                *status = OSKAR_ERR_CUDA_NOT_AVAILABLE;
#endif
            }
            else /* CPU */
            {
                oskar_auto_correlate_scalar_omp_f(n_sources, n_stations,
                        J_, I_, vis_);
            }
        }
    }
}
void oskar_convert_ludwig3_to_theta_phi_components(
        int num_points, const oskar_Mem* phi, int stride, int offset,
        oskar_Mem* vec, int* status)
{
    if (*status) return;
    const int type = oskar_mem_type(phi);
    const int location = oskar_mem_location(phi);
    const int off_h = offset, off_v = offset + 1;
    if (!oskar_mem_is_matrix(vec))
    {
        *status = OSKAR_ERR_TYPE_MISMATCH;
        return;
    }
    if (location == OSKAR_CPU)
    {
        if (type == OSKAR_SINGLE)
            convert_ludwig3_to_theta_phi_float(
                    num_points,
                    oskar_mem_float_const(phi, status),
                    stride, off_h, off_v,
                    oskar_mem_float2(vec, status),
                    oskar_mem_float2(vec, status));
        else if (type == OSKAR_DOUBLE)
            convert_ludwig3_to_theta_phi_double(
                    num_points,
                    oskar_mem_double_const(phi, status),
                    stride, off_h, off_v,
                    oskar_mem_double2(vec, status),
                    oskar_mem_double2(vec, status));
        else
            *status = OSKAR_ERR_BAD_DATA_TYPE;
    }
    else
    {
        size_t local_size[] = {256, 1, 1}, global_size[] = {1, 1, 1};
        const char* k = 0;
        if (type == OSKAR_SINGLE)
            k = "convert_ludwig3_to_theta_phi_float";
        else if (type == OSKAR_DOUBLE)
            k = "convert_ludwig3_to_theta_phi_double";
        else
        {
            *status = OSKAR_ERR_BAD_DATA_TYPE;
            return;
        }
        oskar_device_check_local_size(location, 0, local_size);
        global_size[0] = oskar_device_global_size(
                (size_t) num_points, local_size[0]);
        const oskar_Arg args[] = {
                {INT_SZ, &num_points},
                {PTR_SZ, oskar_mem_buffer_const(phi)},
                {INT_SZ, &stride},
                {INT_SZ, &off_h},
                {INT_SZ, &off_v},
                {PTR_SZ, oskar_mem_buffer(vec)},
                {PTR_SZ, oskar_mem_buffer(vec)}
        };
        oskar_device_launch_kernel(k, location, 1, local_size, global_size,
                sizeof(args) / sizeof(oskar_Arg), args, 0, 0, status);
    }
}
void oskar_mem_add_real(oskar_Mem* mem, double val, int* status)
{
    int precision, location;
    size_t i, num_elements;

    /* Check if safe to proceed. */
    if (*status) return;

    /* Get meta-data. */
    precision = oskar_mem_precision(mem);
    location = oskar_mem_location(mem);

    /* Check for empty array. */
    num_elements = oskar_mem_length(mem);
    if (num_elements == 0)
        return;

    /* Check location. */
    if (location != OSKAR_CPU)
    {
        *status = OSKAR_ERR_BAD_LOCATION;
        return;
    }

    /* Get total number of elements to add. */
    if (oskar_mem_is_matrix(mem))
        num_elements *= 4;

    /* Switch on type and location. */
    if (oskar_mem_is_complex(mem))
    {
        if (precision == OSKAR_DOUBLE)
        {
            double2 *t;
            t = oskar_mem_double2(mem, status);
            for (i = 0; i < num_elements; ++i) t[i].x += val;
        }
        else if (precision == OSKAR_SINGLE)
        {
            float2 *t;
            t = oskar_mem_float2(mem, status);
            for (i = 0; i < num_elements; ++i) t[i].x += val;
        }
        else
        {
            *status = OSKAR_ERR_BAD_DATA_TYPE;
        }
    }
    else
    {
        if (precision == OSKAR_DOUBLE)
        {
            double *t;
            t = oskar_mem_double(mem, status);
            for (i = 0; i < num_elements; ++i) t[i] += val;
        }
        else if (precision == OSKAR_SINGLE)
        {
            float *t;
            t = oskar_mem_float(mem, status);
            for (i = 0; i < num_elements; ++i) t[i] += val;
        }
        else
        {
            *status = OSKAR_ERR_BAD_DATA_TYPE;
        }
    }
}
void oskar_mem_write_fits_cube(oskar_Mem* data, const char* root_name,
        int width, int height, int num_planes, int i_plane, int* status)
{
    oskar_Mem *copy = 0, *ptr = 0;
    size_t len, buf_len;
    char* fname;

    /* Checks. */
    if (*status) return;
    if (oskar_mem_is_matrix(data))
    {
        *status = OSKAR_ERR_BAD_DATA_TYPE;
        return;
    }

    /* Construct the filename. */
    len = strlen(root_name);
    buf_len = 11 + len;
    fname = (char*) calloc(buf_len, sizeof(char));

    /* Copy to host memory if necessary. */
    ptr = data;
    if (oskar_mem_location(data) != OSKAR_CPU)
    {
        copy = oskar_mem_create_copy(ptr, OSKAR_CPU, status);
        ptr = copy;
    }

    /* Deal with complex data. */
    if (oskar_mem_is_complex(ptr))
    {
        oskar_Mem *temp;
        temp = oskar_mem_create(oskar_mem_precision(ptr), OSKAR_CPU,
                oskar_mem_length(ptr), status);

        /* Extract the real part and write it. */
        SNPRINTF(fname, buf_len, "%s_REAL.fits", root_name);
        convert_complex(ptr, temp, 0, status);
        write_pixels(temp, fname, width, height, num_planes, i_plane, status);

        /* Extract the imaginary part and write it. */
        SNPRINTF(fname, buf_len, "%s_IMAG.fits", root_name);
        convert_complex(ptr, temp, 1, status);
        write_pixels(temp, fname, width, height, num_planes, i_plane, status);
        oskar_mem_free(temp, status);
    }
    else
    {
        /* No conversion needed. */
        if ((len >= 5) && (
                !strcmp(&(root_name[len-5]), ".fits") ||
                !strcmp(&(root_name[len-5]), ".FITS") ))
        {
            SNPRINTF(fname, buf_len, "%s", root_name);
        }
        else
        {
            SNPRINTF(fname, buf_len, "%s.fits", root_name);
        }
        write_pixels(ptr, fname, width, height, num_planes, i_plane, status);
    }
    free(fname);
    oskar_mem_free(copy, status);
}
示例#9
0
void oskar_dftw(
        int normalise,
        int num_in,
        double wavenumber,
        const oskar_Mem* weights_in,
        const oskar_Mem* x_in,
        const oskar_Mem* y_in,
        const oskar_Mem* z_in,
        int offset_coord_out,
        int num_out,
        const oskar_Mem* x_out,
        const oskar_Mem* y_out,
        const oskar_Mem* z_out,
        const oskar_Mem* data,
        int offset_out,
        oskar_Mem* output,
        int* status)
{
    if (*status) return;
    const int location = oskar_mem_location(output);
    const int type = oskar_mem_precision(output);
    const int is_dbl = oskar_mem_is_double(output);
    const int is_3d = (z_in != NULL && z_out != NULL);
    const int is_data = (data != NULL);
    const int is_matrix = oskar_mem_is_matrix(output);
    if (!oskar_mem_is_complex(output) || !oskar_mem_is_complex(weights_in) ||
            oskar_mem_is_matrix(weights_in))
    {
        *status = OSKAR_ERR_BAD_DATA_TYPE;
        return;
    }
    if (oskar_mem_location(weights_in) != location ||
            oskar_mem_location(x_in) != location ||
            oskar_mem_location(y_in) != location ||
            oskar_mem_location(x_out) != location ||
            oskar_mem_location(y_out) != location)
    {
        *status = OSKAR_ERR_LOCATION_MISMATCH;
        return;
    }
    if (oskar_mem_precision(weights_in) != type ||
            oskar_mem_type(x_in) != type ||
            oskar_mem_type(y_in) != type ||
            oskar_mem_type(x_out) != type ||
            oskar_mem_type(y_out) != type)
    {
        *status = OSKAR_ERR_TYPE_MISMATCH;
        return;
    }
    if (is_data)
    {
        if (oskar_mem_location(data) != location)
        {
            *status = OSKAR_ERR_LOCATION_MISMATCH;
            return;
        }
        if (!oskar_mem_is_complex(data) ||
                oskar_mem_type(data) != oskar_mem_type(output) ||
                oskar_mem_precision(data) != type)
        {
            *status = OSKAR_ERR_TYPE_MISMATCH;
            return;
        }
    }
    if (is_3d)
    {
        if (oskar_mem_location(z_in) != location ||
                oskar_mem_location(z_out) != location)
        {
            *status = OSKAR_ERR_LOCATION_MISMATCH;
            return;
        }
        if (oskar_mem_type(z_in) != type || oskar_mem_type(z_out) != type)
        {
            *status = OSKAR_ERR_TYPE_MISMATCH;
            return;
        }
    }
    oskar_mem_ensure(output, (size_t) offset_out + num_out, status);
    if (*status) return;
    if (location == OSKAR_CPU)
    {
        if (is_data)
        {
            if (is_matrix)
            {
                if (is_3d)
                {
                    if (is_dbl)
                        dftw_m2m_3d_double(num_in, wavenumber,
                                oskar_mem_double2_const(weights_in, status),
                                oskar_mem_double_const(x_in, status),
                                oskar_mem_double_const(y_in, status),
                                oskar_mem_double_const(z_in, status),
                                offset_coord_out, num_out,
                                oskar_mem_double_const(x_out, status),
                                oskar_mem_double_const(y_out, status),
                                oskar_mem_double_const(z_out, status),
                                oskar_mem_double4c_const(data, status),
                                offset_out,
                                oskar_mem_double4c(output, status), 0);
                    else
                        dftw_m2m_3d_float(num_in, (float)wavenumber,
                                oskar_mem_float2_const(weights_in, status),
                                oskar_mem_float_const(x_in, status),
                                oskar_mem_float_const(y_in, status),
                                oskar_mem_float_const(z_in, status),
                                offset_coord_out, num_out,
                                oskar_mem_float_const(x_out, status),
                                oskar_mem_float_const(y_out, status),
                                oskar_mem_float_const(z_out, status),
                                oskar_mem_float4c_const(data, status),
                                offset_out,
                                oskar_mem_float4c(output, status), 0);
                }
                else
                {
                    if (is_dbl)
                        dftw_m2m_2d_double(num_in, wavenumber,
                                oskar_mem_double2_const(weights_in, status),
                                oskar_mem_double_const(x_in, status),
                                oskar_mem_double_const(y_in, status), 0,
                                offset_coord_out, num_out,
                                oskar_mem_double_const(x_out, status),
                                oskar_mem_double_const(y_out, status), 0,
                                oskar_mem_double4c_const(data, status),
                                offset_out,
                                oskar_mem_double4c(output, status), 0);
                    else
                        dftw_m2m_2d_float(num_in, (float)wavenumber,
                                oskar_mem_float2_const(weights_in, status),
                                oskar_mem_float_const(x_in, status),
                                oskar_mem_float_const(y_in, status), 0,
                                offset_coord_out, num_out,
                                oskar_mem_float_const(x_out, status),
                                oskar_mem_float_const(y_out, status), 0,
                                oskar_mem_float4c_const(data, status),
                                offset_out,
                                oskar_mem_float4c(output, status), 0);
                }
            }
            else
            {
                if (is_3d)
                {
                    if (is_dbl)
                        dftw_c2c_3d_double(num_in, wavenumber,
                                oskar_mem_double2_const(weights_in, status),
                                oskar_mem_double_const(x_in, status),
                                oskar_mem_double_const(y_in, status),
                                oskar_mem_double_const(z_in, status),
                                offset_coord_out, num_out,
                                oskar_mem_double_const(x_out, status),
                                oskar_mem_double_const(y_out, status),
                                oskar_mem_double_const(z_out, status),
                                oskar_mem_double2_const(data, status),
                                offset_out,
                                oskar_mem_double2(output, status), 0);
                    else
                        dftw_c2c_3d_float(num_in, (float)wavenumber,
                                oskar_mem_float2_const(weights_in, status),
                                oskar_mem_float_const(x_in, status),
                                oskar_mem_float_const(y_in, status),
                                oskar_mem_float_const(z_in, status),
                                offset_coord_out, num_out,
                                oskar_mem_float_const(x_out, status),
                                oskar_mem_float_const(y_out, status),
                                oskar_mem_float_const(z_out, status),
                                oskar_mem_float2_const(data, status),
                                offset_out,
                                oskar_mem_float2(output, status), 0);
                }
                else
                {
                    if (is_dbl)
                        dftw_c2c_2d_double(num_in, wavenumber,
                                oskar_mem_double2_const(weights_in, status),
                                oskar_mem_double_const(x_in, status),
                                oskar_mem_double_const(y_in, status), 0,
                                offset_coord_out, num_out,
                                oskar_mem_double_const(x_out, status),
                                oskar_mem_double_const(y_out, status), 0,
                                oskar_mem_double2_const(data, status),
                                offset_out,
                                oskar_mem_double2(output, status), 0);
                    else
                        dftw_c2c_2d_float(num_in, (float)wavenumber,
                                oskar_mem_float2_const(weights_in, status),
                                oskar_mem_float_const(x_in, status),
                                oskar_mem_float_const(y_in, status), 0,
                                offset_coord_out, num_out,
                                oskar_mem_float_const(x_out, status),
                                oskar_mem_float_const(y_out, status), 0,
                                oskar_mem_float2_const(data, status),
                                offset_out,
                                oskar_mem_float2(output, status), 0);
                }
            }
        }
        else
        {
            if (is_3d)
            {
                if (is_dbl)
                    dftw_o2c_3d_double(num_in, wavenumber,
                            oskar_mem_double2_const(weights_in, status),
                            oskar_mem_double_const(x_in, status),
                            oskar_mem_double_const(y_in, status),
                            oskar_mem_double_const(z_in, status),
                            offset_coord_out, num_out,
                            oskar_mem_double_const(x_out, status),
                            oskar_mem_double_const(y_out, status),
                            oskar_mem_double_const(z_out, status),
                            0, offset_out,
                            oskar_mem_double2(output, status), 0);
                else
                    dftw_o2c_3d_float(num_in, (float)wavenumber,
                            oskar_mem_float2_const(weights_in, status),
                            oskar_mem_float_const(x_in, status),
                            oskar_mem_float_const(y_in, status),
                            oskar_mem_float_const(z_in, status),
                            offset_coord_out, num_out,
                            oskar_mem_float_const(x_out, status),
                            oskar_mem_float_const(y_out, status),
                            oskar_mem_float_const(z_out, status),
                            0, offset_out,
                            oskar_mem_float2(output, status), 0);
            }
            else
            {
                if (is_dbl)
                    dftw_o2c_2d_double(num_in, wavenumber,
                            oskar_mem_double2_const(weights_in, status),
                            oskar_mem_double_const(x_in, status),
                            oskar_mem_double_const(y_in, status), 0,
                            offset_coord_out, num_out,
                            oskar_mem_double_const(x_out, status),
                            oskar_mem_double_const(y_out, status), 0,
                            0, offset_out,
                            oskar_mem_double2(output, status), 0);
                else
                    dftw_o2c_2d_float(num_in, (float)wavenumber,
                            oskar_mem_float2_const(weights_in, status),
                            oskar_mem_float_const(x_in, status),
                            oskar_mem_float_const(y_in, status), 0,
                            offset_coord_out, num_out,
                            oskar_mem_float_const(x_out, status),
                            oskar_mem_float_const(y_out, status), 0,
                            0, offset_out,
                            oskar_mem_float2(output, status), 0);
            }
        }
    }
    else
    {
        size_t local_size[] = {256, 1, 1}, global_size[] = {1, 1, 1};
        const void* np = 0;
        const char* k = 0;
        int max_in_chunk;
        float wavenumber_f = (float) wavenumber;

        /* Select the kernel. */
        switch (is_dbl * DBL | is_3d * D3 | is_data * DAT | is_matrix * MAT)
        {
        case D2 | FLT:             k = "dftw_o2c_2d_float";  break;
        case D2 | DBL:             k = "dftw_o2c_2d_double"; break;
        case D3 | FLT:             k = "dftw_o2c_3d_float";  break;
        case D3 | DBL:             k = "dftw_o2c_3d_double"; break;
        case D2 | FLT | DAT:       k = "dftw_c2c_2d_float";  break;
        case D2 | DBL | DAT:       k = "dftw_c2c_2d_double"; break;
        case D3 | FLT | DAT:       k = "dftw_c2c_3d_float";  break;
        case D3 | DBL | DAT:       k = "dftw_c2c_3d_double"; break;
        case D2 | FLT | DAT | MAT: k = "dftw_m2m_2d_float";  break;
        case D2 | DBL | DAT | MAT: k = "dftw_m2m_2d_double"; break;
        case D3 | FLT | DAT | MAT: k = "dftw_m2m_3d_float";  break;
        case D3 | DBL | DAT | MAT: k = "dftw_m2m_3d_double"; break;
        default:
            *status = OSKAR_ERR_FUNCTION_NOT_AVAILABLE;
            return;
        }
        if (oskar_device_is_nv(location))
            local_size[0] = (size_t) get_block_size(num_out);
        oskar_device_check_local_size(location, 0, local_size);
        global_size[0] = oskar_device_global_size(
                (size_t) num_out, local_size[0]);

        /* max_in_chunk must be multiple of 16. */
        max_in_chunk = is_3d ? (is_dbl ? 384 : 800) : (is_dbl ? 448 : 896);
        if (is_data && is_3d && !is_dbl) max_in_chunk = 768;
        const size_t element_size = is_dbl ? sizeof(double) : sizeof(float);
        const size_t local_mem_size = max_in_chunk * element_size;
        const size_t arg_size_local[] = {
                2 * local_mem_size, 2 * local_mem_size,
                (is_3d ? local_mem_size : 0)
        };

        /* Set kernel arguments. */
        const oskar_Arg args[] = {
                {INT_SZ, &num_in},
                {is_dbl ? DBL_SZ : FLT_SZ,
                        is_dbl ? (void*)&wavenumber : (void*)&wavenumber_f},
                {PTR_SZ, oskar_mem_buffer_const(weights_in)},
                {PTR_SZ, oskar_mem_buffer_const(x_in)},
                {PTR_SZ, oskar_mem_buffer_const(y_in)},
                {PTR_SZ, is_3d ? oskar_mem_buffer_const(z_in) : &np},
                {INT_SZ, &offset_coord_out},
                {INT_SZ, &num_out},
                {PTR_SZ, oskar_mem_buffer_const(x_out)},
                {PTR_SZ, oskar_mem_buffer_const(y_out)},
                {PTR_SZ, is_3d ? oskar_mem_buffer_const(z_out) : &np},
                {PTR_SZ, is_data ? oskar_mem_buffer_const(data) : &np},
                {INT_SZ, &offset_out},
                {PTR_SZ, oskar_mem_buffer(output)},
                {INT_SZ, &max_in_chunk}
        };
        oskar_device_launch_kernel(k, location, 1, local_size, global_size,
                sizeof(args) / sizeof(oskar_Arg), args,
                sizeof(arg_size_local) / sizeof(size_t), arg_size_local,
                status);
    }
    if (normalise)
        oskar_mem_scale_real(output, 1. / num_in, offset_out, num_out, status);
}
void oskar_element_evaluate(const oskar_Element* model, oskar_Mem* output,
        double orientation_x, double orientation_y, int num_points,
        const oskar_Mem* x, const oskar_Mem* y, const oskar_Mem* z,
        double frequency_hz, oskar_Mem* theta, oskar_Mem* phi, int* status)
{
    int element_type, taper_type, freq_id;
    double dipole_length_m;

    /* Check if safe to proceed. */
    if (*status) return;

    /* Check that the output array is complex. */
    if (!oskar_mem_is_complex(output))
    {
        *status = OSKAR_ERR_BAD_DATA_TYPE;
        return;
    }

    /* Resize output array if required. */
    if ((int)oskar_mem_length(output) < num_points)
        oskar_mem_realloc(output, num_points, status);

    /* Get the element model properties. */
    element_type    = model->element_type;
    taper_type      = model->taper_type;
    dipole_length_m = model->dipole_length;
    if (model->dipole_length_units == OSKAR_WAVELENGTHS)
        dipole_length_m *= (C_0 / frequency_hz);

    /* Check if element type is isotropic. */
    if (element_type == OSKAR_ELEMENT_TYPE_ISOTROPIC)
        oskar_mem_set_value_real(output, 1.0, 0, 0, status);

    /* Ensure there is enough space in the theta and phi work arrays. */
    if ((int)oskar_mem_length(theta) < num_points)
        oskar_mem_realloc(theta, num_points, status);
    if ((int)oskar_mem_length(phi) < num_points)
        oskar_mem_realloc(phi, num_points, status);

    /* Compute modified theta and phi coordinates for dipole X. */
    oskar_convert_enu_directions_to_theta_phi(num_points, x, y, z,
            M_PI_2 - orientation_x, theta, phi, status);

    /* Evaluate polarised response if output array is matrix type. */
    if (oskar_mem_is_matrix(output))
    {
        /* Check if spline data present for dipole X. */
        if (oskar_element_has_x_spline_data(model))
        {
            /* Get the frequency index. */
            freq_id = oskar_find_closest_match_d(frequency_hz,
                    oskar_element_num_freq(model),
                    oskar_element_freqs_hz_const(model));

            /* Evaluate spline pattern for dipole X. */
            oskar_splines_evaluate(output, 0, 8, model->x_h_re[freq_id],
                    num_points, theta, phi, status);
            oskar_splines_evaluate(output, 1, 8, model->x_h_im[freq_id],
                    num_points, theta, phi, status);
            oskar_splines_evaluate(output, 2, 8, model->x_v_re[freq_id],
                    num_points, theta, phi, status);
            oskar_splines_evaluate(output, 3, 8, model->x_v_im[freq_id],
                    num_points, theta, phi, status);

            /* Convert from Ludwig-3 to spherical representation. */
            oskar_convert_ludwig3_to_theta_phi_components(output, 0, 4,
                    num_points, phi, status);
        }
        else if (element_type == OSKAR_ELEMENT_TYPE_DIPOLE)
        {
            /* Evaluate dipole pattern for dipole X. */
            oskar_evaluate_dipole_pattern(output, num_points, theta, phi,
                    frequency_hz, dipole_length_m, 0, 4, status);
        }
        else if (element_type == OSKAR_ELEMENT_TYPE_GEOMETRIC_DIPOLE)
        {
            /* Evaluate dipole pattern for dipole X. */
            oskar_evaluate_geometric_dipole_pattern(output, num_points,
                    theta, phi, 0, 4, status);
        }

        /* Compute modified theta and phi coordinates for dipole Y. */
        oskar_convert_enu_directions_to_theta_phi(num_points, x, y, z,
                M_PI_2 - orientation_y, theta, phi, status);

        /* Check if spline data present for dipole Y. */
        if (oskar_element_has_y_spline_data(model))
        {
            /* Get the frequency index. */
            freq_id = oskar_find_closest_match_d(frequency_hz,
                    oskar_element_num_freq(model),
                    oskar_element_freqs_hz_const(model));

            /* Evaluate spline pattern for dipole Y. */
            oskar_splines_evaluate(output, 4, 8, model->y_h_re[freq_id],
                    num_points, theta, phi, status);
            oskar_splines_evaluate(output, 5, 8, model->y_h_im[freq_id],
                    num_points, theta, phi, status);
            oskar_splines_evaluate(output, 6, 8, model->y_v_re[freq_id],
                    num_points, theta, phi, status);
            oskar_splines_evaluate(output, 7, 8, model->y_v_im[freq_id],
                    num_points, theta, phi, status);

            /* Convert from Ludwig-3 to spherical representation. */
            oskar_convert_ludwig3_to_theta_phi_components(output, 2, 4,
                    num_points, phi, status);
        }
        else if (element_type == OSKAR_ELEMENT_TYPE_DIPOLE)
        {
            /* Evaluate dipole pattern for dipole Y. */
            oskar_evaluate_dipole_pattern(output, num_points, theta, phi,
                    frequency_hz, dipole_length_m, 2, 4, status);
        }
        else if (element_type == OSKAR_ELEMENT_TYPE_GEOMETRIC_DIPOLE)
        {
            /* Evaluate dipole pattern for dipole Y. */
            oskar_evaluate_geometric_dipole_pattern(output, num_points,
                    theta, phi, 2, 4, status);
        }
    }

    /* Scalar response. */
    else
    {
        /* Check if scalar spline data present. */
        if (oskar_element_has_scalar_spline_data(model))
        {
            /* Get the frequency index. */
            freq_id = oskar_find_closest_match_d(frequency_hz,
                    oskar_element_num_freq(model),
                    oskar_element_freqs_hz_const(model));

            oskar_splines_evaluate(output, 0, 2, model->scalar_re[freq_id],
                    num_points, theta, phi, status);
            oskar_splines_evaluate(output, 1, 2, model->scalar_im[freq_id],
                    num_points, theta, phi, status);
        }
        else if (element_type == OSKAR_ELEMENT_TYPE_DIPOLE)
        {
            oskar_evaluate_dipole_pattern(output, num_points, theta, phi,
                    frequency_hz, dipole_length_m, 0, 1, status);
        }
        else if (element_type == OSKAR_ELEMENT_TYPE_GEOMETRIC_DIPOLE)
        {
            oskar_evaluate_geometric_dipole_pattern(output, num_points,
                    theta, phi, 0, 1, status);
        }
    }

    /* Apply element tapering, if specified. */
    if (taper_type == OSKAR_ELEMENT_TAPER_COSINE)
    {
        oskar_apply_element_taper_cosine(output, num_points,
                model->cosine_power, theta, status);
    }
    else if (taper_type == OSKAR_ELEMENT_TAPER_GAUSSIAN)
    {
        oskar_apply_element_taper_gaussian(output, num_points,
                model->gaussian_fwhm_rad, theta, status);
    }
}
void oskar_imager_update_plane_dft(oskar_Imager* h, size_t num_vis,
        const oskar_Mem* uu, const oskar_Mem* vv, const oskar_Mem* ww,
        const oskar_Mem* amps, const oskar_Mem* weight, oskar_Mem* plane,
        double* plane_norm, int* status)
{
    size_t i, num_pixels;
    oskar_Thread** threads = 0;
    ThreadArgs* args = 0;
    if (*status) return;

    /* Check the image plane. */
    num_pixels = (size_t) h->image_size;
    num_pixels *= num_pixels;
    if (oskar_mem_precision(plane) != h->imager_prec)
        *status = OSKAR_ERR_TYPE_MISMATCH;
    if (oskar_mem_is_complex(plane) || oskar_mem_is_matrix(plane))
        *status = OSKAR_ERR_BAD_DATA_TYPE;
    oskar_mem_ensure(plane, num_pixels, status);
    if (*status) return;

    /* Set up worker threads. */
    const size_t num_threads = (size_t) (h->num_devices);
    threads = (oskar_Thread**) calloc(num_threads, sizeof(oskar_Thread*));
    args = (ThreadArgs*) calloc(num_threads, sizeof(ThreadArgs));
    for (i = 0; i < num_threads; ++i)
    {
        args[i].h = h;
        args[i].thread_id = (int) i;
        args[i].num_vis = (int) num_vis;
        args[i].uu = uu;
        args[i].vv = vv;
        args[i].ww = ww;
        args[i].amp = amps;
        args[i].weight = weight;
        args[i].plane = plane;
    }

    /* Set status code. */
    h->status = *status;

    /* Start the worker threads. */
    h->i_block = 0;
    for (i = 0; i < num_threads; ++i)
        threads[i] = oskar_thread_create(run_blocks, (void*)&args[i], 0);

    /* Wait for worker threads to finish. */
    for (i = 0; i < num_threads; ++i)
    {
        oskar_thread_join(threads[i]);
        oskar_thread_free(threads[i]);
    }
    free(threads);
    free(args);

    /* Get status code. */
    *status = h->status;

    /* Update normalisation. */
    if (oskar_mem_precision(weight) == OSKAR_DOUBLE)
    {
        const double* w = oskar_mem_double_const(weight, status);
        for (i = 0; i < num_vis; ++i) *plane_norm += w[i];
    }
    else
    {
        const float* w = oskar_mem_float_const(weight, status);
        for (i = 0; i < num_vis; ++i) *plane_norm += w[i];
    }
}