Related
I am having issues returning a 2D array from a C extension back to Python. When I allocate memory using malloc the returned data is rubbish. When I just initialise an array like sol_matrix[nt][nvar] the returned data is as expected.
#include <Python.h>
#include <numpy/arrayobject.h>
#include <math.h>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
// function to be solved by Euler solver
double func (double xt, double y){
double y_temp = pow(xt, 2);
y = y_temp;
return y;
}
static PyObject* C_Euler(double h, double xn)
{
double y_temp, dydx; //temps required for solver
double y_sav = 0; //temp required for solver
double xt = 0; //starting value for xt
int nvar = 2; //number of variables (including time)
int nt = xn/h; //timesteps
double y = 0; //y starting value
//double sol_matrix[nt][nvar]; //works fine
double **sol_matrix = malloc(nt * sizeof(double*)); //doesn't work
for (int i=0; i<nt; ++i){
sol_matrix[i] = malloc (nvar * sizeof(double));
}
int i=0;
//solution loop - Euler method.
while (i < nt){
sol_matrix[i][0]=xt;
sol_matrix[i][1]=y_sav;
dydx = func(xt, y);
y_temp = y_sav + h*dydx;
xt = xt+h;
y_sav=y_temp;
i=i+1;
}
npy_intp dims[2];
dims[0] = nt;
dims[1] = 2;
//Create Python object to copy solution array into, get pointer to
//beginning of array, memcpy the data from the C colution matrix
//to the Python object.
PyObject *newarray = PyArray_SimpleNew(2, dims, NPY_DOUBLE);
double *p = (double *) PyArray_DATA(newarray);
memcpy(p, sol_matrix, sizeof(double)*(nt*nvar));
// return array to Python
return newarray;
}
static PyObject* Euler(PyObject* self, PyObject* args)
{
double h, xn;
if (!PyArg_ParseTuple(args, "dd", &h, &xn)){
return NULL;
}
return Py_BuildValue("O", C_Euler(h,xn));
}
Could you provide any guidance on where I am going wrong?
Thank you.
The data in sol_matrix is not in contiguous memory, it's in nt separately allocated arrays. Therefore the line
memcpy(p, sol_matrix, sizeof(double)*(nt*nvar));
is not going to work.
I'm not a big fan of pointer-to-pointer arrays so believe your best option is to allocate sol_matrix as one big chunk:
double *sol_matrix = malloc(nt*nvar * sizeof(double));
This does mean you can't do 2D indexing so will need to do
// OLD: sol_matrix[i][0]=xt;
sol_matrix[i*nvar + 0] = xt;
In contrast
double sol_matrix[nt][nvar]; //works fine
is a single big chunk of memory so the copy works fine.
So I am currently writing a function that can output data to an array ready for export to text. Function works fine when collecting variables but an getting the "Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)" error within Xcode and don't know how to debug this. I have tried using calloc to assign memory to the array and using address locations but am still getting similar error messages and using address locations just don't work.
Has anyone got any suggestions for how I can solve this? The error code is showing itself on the first line of the for loop and this function is running as part of a larger function.
void LamVelProf(double dP, double L, double d, double mu)
{
double *r = malloc(sizeof(r)); //Point radius from centreline
double *R = malloc(sizeof(R)); //Absolute pipe radius
double *vx = malloc(sizeof(vx));
double *gvx = malloc(sizeof(gvx));
double *offset = malloc(sizeof(offset));
double **profile[7500][4];
**profile = calloc((7500*4), sizeof(double));
//double **profile = calloc((7500*4), sizeof(double));
int *i = malloc(sizeof(i));
*R = d/2; //Setting the boundary condition
*offset = 0.001;
*i = 0;
for(*r = 0; *r < (*R + (*offset/2)); *r = (*r)+(*offset))
{
**profile[*i][0] = *r;
LamVelProCalc(dP, L, d, mu, *r, vx);
**profile[*i][1] = *vx;
LamGenProCalc(*r, d, gvx);
**profile[*i][2] = *gvx;//Results from general profile
**profile[*i][3] = *i+1;
++*i; //Increasing count by 1
}
printf("%i rows generated\n", *i);
free(r);
free(R);
free(offset);
int *row = malloc(sizeof(row));
int *col = malloc(sizeof(col));
for(*row = 0; *row < *i + 1; *row = *row + 1)
{
for(*col = 0; *col < 4; *col = *col + 1)
{
printf("%f", **profile[*row][*col]);
if(*col == 3)
{
printf("\n");
}else{
printf("\t");
}
}
}
}
I've had to aggressively de-pointerize this code to bring it back into the realm of understandability, and the end result is this:
void LamVelProf(double dP, double L, double d, double mu)
{
double R = d/2;
double offset = 0.001;
double profile[7500][4];
int i = 0;
for (double r = 0; r < (R + (offset/2)); r += offset) {
double vx = 0.0; // Initialize appropriately
double gvx = 0.0;
profile[i][0] = r;
// No idea what this does, or why the return value is ignored
LamVelProCalc(dP, L, d, mu, r, vx);
profile[i][1] = vx;
// No idea what this does, or why the return value is ignored
LamGenProCalc(r, d, gvx);
profile[i][2] = gvx;//Results from general profile
profile[i][3] = i+1;
++i; //Increasing count by 1
}
printf("%i rows generated\n", i);
for(int row = 0; row < i + 1; ++row)
{
for(int col = 0; col < 4; ++col)
{
printf("%f", profile[row][col]);
if (col == 3) {
printf("\n");
} else {
printf("\t");
}
}
}
}
As you can see there's two function calls buried in there that should probably have pointer arguments, my guess is vx and gvx are intended to be manipulated by that function. In C it is common to use use pointers to manipulate external variables, so a pointer argument almost always means "array" or "mutable argument" depending on context.
In other words I'd expect to see:
LamVelProCalc(dP, L, d, mu, r, &vx);
Or even better:
double vx = LamVelProCalc(dP, L, d, mu, r);
Where that value is explicitly returned instead.
This should compile and run without crashing now, though note the above mentioned issues.
When it comes to compiler suggestions to fix a problem, remember to take them all under advisement. At the end of the day you're the programmer, not the compiler, and not every educated guess it makes will be a valid interpretation of the problem at hand. If you unwaveringly follow the compiler's advice it may lead you down really, really strange paths, as perhaps has happened here.
As a note, having variables r and R is borderline programmer abuse. Please don't do this.
Another thing to keep in mind is your rather arbitrary use of 7500 here. Is that just a wild guess as to how many entries you'll need? It's almost always better to compute that, you know how the for loop will run in advance so you can do the math, and allocate accordingly.
If it is a limit you've arrived at through some other method it's worth using a #define to indicate as such, like:
#define MAX_PROFILE_ENTRIES 7500
Where it's now clear what the meaning behind that number is.
So after a lot of head scratching and realising I didn't need malloc because my array would definitely never reach the 125000 elements. Thanks to #tadman for helping with that. Here's the final program which is designed to be called from a menu function:
//
// 02g1LamVelPro .c
// Process Model (MacOS Version)
//
// Created by --- on 30/06/2020.
// Copyright © 2020 ---. All rights reserved.
//
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define maxstrlen 128
//Declaring global variables and allocating memory
//Function Output
double profile; //Array of doubles
//Calculation Variables
double dP;
double L;
double d;
double mu;
double r;
//Miscellaneous Variables
void LamVelProVar(double *dP, double *L, double *d, double *mu)
{
//Declaring input variables
char pres[maxstrlen];
char len[maxstrlen];
char dia[maxstrlen];
char visc[maxstrlen];
printf("Fluid pressure loss (Pa) = ");
*dP = atof(fgets(pres, sizeof(pres), stdin));
printf("Pipe length (m) = ");
*L = atof(fgets(len, sizeof(len), stdin));
printf("Pipe diameter (mm) = ");
*d = atof(fgets(dia, sizeof(dia), stdin));
*d = (*d)*0.001;
printf("Fluid viscosity (cP) = ");
*mu = atof(fgets(visc, sizeof(visc), stdin));
*mu = (*mu)*0.001;
fflush(stdout);
}
double LamVelCalc(double dP, double L, double d, double mu, double r, double *v_x)
{
//Calculation of the theoretical velocity profile with the flow possessing laminar characteristics
double frac1;
double frac2;
double frac3;
frac1 = (dP/L);
frac2 = pow(d,2);
frac2 = (frac2)/(16*mu);
frac3 = 2*r;
frac3 = (frac3)/d;
frac3 = pow(frac3, 2);
frac3 = 1 - (frac3);
*v_x = frac1 * frac2;
*v_x = (*v_x) * frac3;
return *v_x;
}
double LamGenCalc(double r, double d, double *func)
{
//Calculation of the general velocity profile with the flow possessing laminar characteristics
*func = 2*r;
*func = (*func)/d;
*func = pow(*func, 2);
*func = 1 - (*func);
return *func; //Returns v/v_max
}
double **LamVelProfCalc(double dP, double L, double d, double mu)
{
char display[maxstrlen];
double v_x = 0;
double offset = 0.0001;
//Calculating number of rows for the profile results matrix
double prad = d/2;
int whildisp = 1;
int rows = ((prad)/ (offset)) + 1;
printf("%i rows required\n", rows);
double profile[rows][3];
int i = 0;
for(double r = 0.0; r < (prad + (offset/2)); r += offset)
{
profile[i][0] = r; //Displaying point radius
profile[i][1] = LamVelCalc(dP, L, d, mu, r, &v_x); //Calculating point velocity
profile[i][2] = LamGenCalc(r, d, &v_x); //Calculating
//profile[i][3] = i + 1;
++i;
}
printf("%i rows successfully generated\n\n", i);
while(whildisp == 1)
{
printf("Do you want to display the generated data? ");
fgets(display, sizeof(display), stdin);
switch(display[0])
{
case '1':
case 'Y':
case 'y':
printf("Displaying data\n");
printf("Inputted variables:\n");
printf("dP =\t%.3f\tPa\n", dP);
printf("L =\t%.3f\tm\n", L);
printf("d =\t%.1f\tmm\n", d*1000);
printf("mu =\t%.3f\tPa.s\n", mu);
printf("v_max =\t%.3f\tm/s\n\n", LamVelCalc(dP, L, d, mu, 0, &v_x));
printf("r (m)\tv_x (m/s)\tv/v_max\n");
int row = 0;
int col = 0;
for(row = 0; row < i; ++row)
{
for(col = 0; col < 3; ++col)
{
printf("%.5f", profile[row][col]);
if(col == 2)
{
printf("\n");
}else{
printf("\t");
}
}
}
whildisp = 0;
break;
case '0':
case 'N':
case 'n':
whildisp = 0;
default:
printf("Input not recognised.\n");
break;
}
}
return profile;
}
void LamVelPro()
{
//Main Function
char ContCond[maxstrlen];
int whilmain = 1;
printf("Laminar flow velocity profile\n");
while(whilmain == 1)
{
//Variable declaration
double dP;
double L;
double d;
double mu;
double r;
//Data collection
LamVelProVar(&dP, &L, &d, &mu);
//Data manipulation
LamVelProfCalc(dP, L, d, mu);
//Ask for file write (Remember while loop)
//...
//Continue function
int whilcont = 1;
while(whilcont == 1)
{
printf("Do you want to continue? ");
fgets(ContCond, sizeof(ContCond), stdin);
switch(ContCond[0])
{
case '1':
case 'T':
case 'Y':
case 't':
case 'y':
whilcont = 0;
break;
case '0':
case 'F':
case 'N':
case 'f':
case 'n':
whilcont = 0;
whilmain = 0;
break;
default:
printf("Input not recognised\n");
break;
}
}
}
fflush(stdout);
}
I am using Ruby Fiddle to access a C function to perform some heavy calculations. The C function works perfectly well when called directly, but when used via Fiddle it returns various rows of nans and infs in an unpredictable way. The function operates on matrices that are passed as pointers to arrays.
I have debugged the C code and everything works fine. I have also saved the various parameters passed to the C function to file to be sure that Fiddle was not passing some weird values, but there is no obvious (at least for me) problem.
Additionally it seems that for 'smaller' matrices this does not happen.
Apologies in advance for the code being very long, but this is the only way to exactly reproduce what it is happening. Data for testing is in this file. (gist). You can just copy and paste the Ruby and C test data to the code below.
If it helps, I am working on macos Catalina and Ruby 2.2.4 (requirement).
The C code is the following:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
double *mrt(
double *person_total_shortwave,
int rows_pts, int cols_pts,
double *dry_bulb_temperatures,
double *ground_temperatures,
double *atmospheric_longwave,
double *sky_view_factors,
double *shading_view_factors_matrix,
int rows_svf, int cols_svf,
double *shading_temperatures_matrix,
int rows_st, int cols_st,
int size_dbt,
double person_emissivity_shortwave, double person_emissivity_longwave,
double surroundings_emissivity, double ground_emissivity, double ground_person_view_factor);
int save_to_file(char *filename, int m, int n, double *mat)
{
FILE *fp;
int i, j;
if ((fp = freopen(filename, "w", stdout)) == NULL)
{
printf("Cannot open file.\n");
exit(1);
}
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
if (j == n - 1)
printf("%.17g \n", mat[i * n + j]);
else
printf("%.17g ", mat[i * n + j]);
fclose(fp);
return 0;
}
int main(void)
{
// REPLACE WITH C DATA FROM FILE
double *mrt_result;
mrt_result = mrt(person_total_shortwave[0],
rows_pts, cols_pts,
dry_bulb_temperatures,
ground_temperatures,
atmospheric_longwave,
sky_view_factors,
shading_view_factors_matrix[0],
rows_svf, cols_svf,
shading_temperatures_matrix[0],
rows_st, cols_st,
size_dbt,
person_emissivity_shortwave, person_emissivity_longwave,
surroundings_emissivity, ground_emissivity, ground_person_view_factor);
// save_to_file(rows_pts, cols_pts, mrt_result);
return 0;
}
// https://www.dropbox.com/s/ix06edrrctad421/Calculation%20of%20Mean%20Radiant%20Temperature3.odt?dl=0
double *mrt(
double *person_total_shortwave,
int rows_pts, int cols_pts,
double *dry_bulb_temperatures,
double *ground_temperatures,
double *atmospheric_longwave,
double *sky_view_factors,
double *shading_view_factors_matrix,
int rows_svf, int cols_svf,
double *shading_temperatures_matrix,
int rows_st, int cols_st,
int size_dbt,
double person_emissivity_shortwave, double person_emissivity_longwave,
double surroundings_emissivity, double ground_emissivity, double ground_person_view_factor)
{
save_to_file("PTS.txt", rows_pts, cols_pts, person_total_shortwave);
save_to_file("SVF.txt", 1, cols_svf, sky_view_factors);
save_to_file("SVSM.txt", rows_svf, cols_svf, shading_view_factors_matrix);
save_to_file("STM.txt", rows_st, cols_st, shading_temperatures_matrix);
double *mrt_mat = (double *)calloc(rows_pts * cols_pts, sizeof(double));
save_to_file("MRT_MAT0.txt", rows_pts, cols_pts, mrt_mat);
int t, c, k;
double sigma = 5.67E-8;
double body_area = 1.94;
double tmrt4_shortwave, tmrt4_longwave_ground, tmrt4_atm_longwave, tmrt4_surroundings;
double tmrt4_shading[rows_svf];
memset(tmrt4_shading, 0.0, rows_svf * sizeof(double));
double tmrt4;
double surroundings_view_factor;
// t runs through the timesteps
// c runs through the points in the mesh
for (t = 0; t < rows_pts; t++)
{
for (c = 0; c < cols_pts; c++)
{
tmrt4_shortwave = 1.0 / (sigma * body_area) * (person_emissivity_shortwave / person_emissivity_longwave) * person_total_shortwave[t * cols_pts + c];
// We are assuming that the ground is at ambient temperature
tmrt4_longwave_ground = ground_person_view_factor * ground_emissivity * pow((273.15 + ground_temperatures[t]), 4);
// Here we are using the actual long wave radiation from the sky
tmrt4_atm_longwave = (1.0 - ground_person_view_factor) / sigma * sky_view_factors[c] * atmospheric_longwave[t];
// We need to remove the contribution of all the shading devices
// k runs through the shading devices
surroundings_view_factor = 1.0 - sky_view_factors[c];
for (k = 0; k < rows_svf; k++)
{
surroundings_view_factor -= shading_view_factors_matrix[k * cols_svf + c];
}
tmrt4_surroundings = (1.0 - ground_person_view_factor) * surroundings_view_factor * surroundings_emissivity * pow((273.15 + dry_bulb_temperatures[t] - 0.0), 4);
// We now need to account for all contributions of the shading devices
for (k = 0; k < rows_svf; k++)
{
tmrt4_shading[k] = (1.0 - ground_person_view_factor) * (shading_view_factors_matrix[k * cols_svf + c]) * surroundings_emissivity * pow((273.15 + shading_temperatures_matrix[k * cols_svf + t]), 4);
}
// Finally we add them all (see paper) for the total contribution
tmrt4 = tmrt4_shortwave + tmrt4_longwave_ground + tmrt4_atm_longwave + tmrt4_surroundings;
for (k = 0; k < rows_svf; k++)
{
tmrt4 += tmrt4_shading[k];
}
// Just convert to celsius
mrt_mat[t * cols_pts + c] = pow(tmrt4, 0.25) - 273.15;
}
}
save_to_file("MRT_MAT.txt", rows_pts, cols_pts, mrt_mat);
// double x = 1.5;
// while (1)
// {
// x *= sin(x) / atan(x) * tanh(x) * sqrt(x);
// }
return mrt_mat;
}
and I compile is with clang -g --extra-warnings utils.c -o utils.
The Ruby code is the following
require "fiddle"
require "fiddle/import"
module RG
extend Fiddle::Importer
#handler.handlers.each { |h| h.close unless h.close_enabled? } unless #handler.nil?
GC.start
dlload File.join("utils")
extern "double* mrt(double*, int, int, double*, double*, double*, double*, double*, int, int, double*, int, int, int, double, double, double, double, double)"
def self.mat_to_ptr(matrix)
return Fiddle::Pointer[matrix.flatten.pack("E*")]
end
def self.ptr_to_mat(ptr, rows, cols)
length = rows * cols * Fiddle::SIZEOF_DOUBLE
mat = ptr[0, length]
return mat.unpack("E*").each_slice(cols).to_a
end
def self.mean_radiant_temperature(
person_total_shortwave,
dry_bulb_temperatures,
ground_temperatures,
atmospheric_longwave,
sky_view_factors,
shading_view_factors_matrix,
shading_temperatures_matrix,
person_emissivity_shortwave,
person_emissivity_longwave,
surroundings_emissivity,
ground_emissivity,
ground_person_view_factor
)
rows_pts = person_total_shortwave.size
cols_pts = person_total_shortwave[0].size
person_total_shortwave_pointer = RG.mat_to_ptr(person_total_shortwave)
dry_bulb_temperatures_pointer = RG.mat_to_ptr(dry_bulb_temperatures)
ground_temperatures_pointer = RG.mat_to_ptr(ground_temperatures)
size_dbt = dry_bulb_temperatures.size
atmospheric_longwave_pointer = RG.mat_to_ptr(atmospheric_longwave)
sky_view_factors_pointer = RG.mat_to_ptr(sky_view_factors)
rows_svf = shading_view_factors_matrix.size
if rows_svf > 0
cols_svf = shading_view_factors_matrix[0].size
else
cols_svf = 0
end
shading_view_factors_matrix_pointer = RG.mat_to_ptr(shading_view_factors_matrix)
rows_st = shading_temperatures_matrix.size
if rows_st > 0
cols_st = shading_temperatures_matrix[0].size
else
cols_st = 0
end
shading_temperatures_matrix_pointer = RG.mat_to_ptr(shading_temperatures_matrix)
mrt_pointer = mrt(
person_total_shortwave_pointer,
rows_pts, cols_pts,
dry_bulb_temperatures_pointer,
ground_temperatures_pointer,
atmospheric_longwave_pointer,
sky_view_factors_pointer,
shading_view_factors_matrix_pointer,
rows_svf, cols_svf,
shading_temperatures_matrix_pointer,
rows_st, cols_st,
size_dbt,
person_emissivity_shortwave,
person_emissivity_longwave,
surroundings_emissivity,
ground_emissivity,
ground_person_view_factor
)
return RG.ptr_to_mat(mrt_pointer, rows_pts, cols_pts)
end
end
// REPLACE WITH RUBY DATA FROM FILE
mean_radiant_temperatures = RG.mean_radiant_temperature(
person_total_shortwave,
dry_bulb_temperatures,
ground_temperatures,
atmospheric_longwave,
sky_view_factors,
shading_view_factors_matrix,
shading_temperatures_matrix,
person_emissivity_shortwave,
person_emissivity_longwave,
surroundings_emissivity,
ground_emissivity,
ground_person_view_factor
)
File.open("MRT_MAT_RUBY.txt", "w") do |f|
mean_radiant_temperatures.each do |row|
f.puts row.join(' ')
end
end
If you want to test it, first launch ./utils. It will save a few files on the local folder. Have a look at MRT_MAT.txt.
Now launch the ruby code several times. It will generate the same file, but you will notice that "often" the file will contain random rows with nan and inf. The same data is returned to Ruby and saved in the file MRT_MAT_RUBY.txt in the local directory.
I am quite familiar with Ruby, but C is not really my strength. It would be great being able to debug the C code when called from Ruby, but I don't really know how to do it.
I guess the string created by pack method in mat_to_ptr method is collected by GC.
You should keep the string until pte_to_mat is called.
I am constantly facing a fatal error when calling a C function in R and I suspect it may be because of the way I have used "realloc" routine for variable n_k in the gCRSF_gibbs function. Can somebody tell me if the reallocation of memory to n_k is correct or not?
void gCRSF_gibbs(double *z, double **n_k, double *SampleDex,
double *r, double *a, double *p,
int *Ksize, int *WordNum) {
int i, j, k;
double mass;
double *prob_cumsum;
double cum_sum, probrnd;
prob_cumsum = (double *) calloc(Ksize[0],sizeof(double));
mass = r[0]*pow(p[0],-a[0]);
for (i=0;i<WordNum[0];i++){
j = (int) SampleDex[i] -1;
k = (int) z[j] -1;
if(z[j]>0){
(*n_k)[k]--;
}
for (cum_sum=0, k=0; k<Ksize[0]; k++) {
cum_sum += (*n_k)[k]-a[0];
prob_cumsum[k] = cum_sum;
}
if ( ((double) rand() / RAND_MAX * (cum_sum + mass) < cum_sum)){
probrnd = (double)rand()/(double)RAND_MAX*cum_sum;
k = BinarySearch(probrnd, prob_cumsum, Ksize[0]);
}
else{
for (k=0; k<Ksize[0]; k++){
if ((int) (*n_k)[k]==0){
break;
}
}
if (k==Ksize[0]){
Ksize[0]++;
realloc(*n_k,sizeof(**n_k)*Ksize[0]);
(*n_k)[Ksize[0]-1]=0;
prob_cumsum = realloc(prob_cumsum,sizeof(*prob_cumsum)*Ksize[0]);
}
}
z[j] = k+1;
(*n_k)[k]++;
}
free(prob_cumsum);}
And this is how it is called in R:
gCRSF_gibbs <- function(z, n_k, sampleDex, r, a, p){
out <- .C("gCRSF_gibbs", z=as.double(z), n_k=as.double(n_k),
SampleDex=as.double(sampleDex), r=as.double(r), a=as.double(a),
p=as.double(p), Ksize=as.integer(length(n_k)),
WordNum=as.integer(length(sampleDex)))
out}
You're using realloc wrong. It should be:
*n_k = realloc(*n_k,sizeof(**n_k)*Ksize[0]);
You always want to use realloc like p = realloc(p, size). Otherwise, if the buffer gets moved by realloc, *n_k will be pointing to a freed pointer.
So I'm trying to write a function that will return an array of several values. At the moment, it is running correctly but only outputting the final calculated value. How would I make it so the output includes all calculated values?
My code looks like this:
//Practice to output an array of structs
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct boat_params {
double V, Uc, Vc;
};
struct boat_params submerged_volume(double L1, double L2, double Lavg, double H) {
struct boat_params volume;
double V_sub, Uc_sub, Vc_sub;
V_sub = 0;
//Boat description
double C, delta;
double theta, theta_rad, theta_min, theta_min2, theta_lim, theta_lim2, theta_lim_deg;
double Ug1, Ug2, Vg1, Vg2, V1, V2;
double pi;
pi = 4*atan(1);
C = sqrt(L1*L1 + L2*L2);
delta = acos(L1/C);
theta_lim = asin(H/L1);
theta_lim_deg = (theta_lim/pi) * 180.0;
theta_min = asin(H/C) - delta;
theta_min2 = 0;
//Calculating the submerged volume and centre of gravity for each different angle
for (theta = 0; theta <= 10; theta ++) {
//**Note: I've taken out the actual calculations of V_sub, Uc_sub, and Vc_sub for brevity**
volume.V = V_sub;
volume.Uc = Uc_sub;
volume.Vc = Vc_sub;
}
return volume;
}
int main () {
double L1, L2, Lavg, H;
struct boat_params volume;
L1 = 17.6;
L2 = 3;
Lavg = 4;
H = 4.5;
volume = submerged_volume(L1, L2, Lavg, H);
printf("V = %lf\nUc = %lf\nVc = %lf\n", volume.V, volume.Uc, volume.Vc);
return 0;
}
I can get it to correctly output the last calculated value (for theta = 10) but that's the only value I'm getting. How would I calculate V_sub, Uc_sub, and Vc_sub for each theta value? and output each value. I'm assuming this means turning the struct into an array and filling each element of the array with values of the struct for that theta but I don't know how to do this!
I really appreciate any help and thank you in advance.
Also: If possible I'd like to avoid pointers but understand this may not be possible! I'm still very new and not good at using them!
You are quite right, you will need to have an array for that. If the number of elements in the array is constant, you could also create a struct that contains exactly that number elements, but please don't do that.
To operate on arrays you will - unfortunately - need pointers. A very common way to do this in C is not to return a pointer, but pass a 'result' pointer in. This means that it will be up to the user of the function to allocate space and free it, he can also use the syntax for arrays. In your code it seems that the number of values is constant, this makes the aforementioned solution possible. Alternatively you could allocate space on the heap (using malloc) and return a pointer, but that means the user needs to free memory he never allocated, counter intuitive and might result in memory leaks if he forgets to do so. Consider the following solution:
void submerged_volume(double L1, double L2, double Lavg, double H, struct boat_params *result) {
// your calculations here
for (theta = 0; theta <= 10; theta ++) {
(result+theta)->V = V_sub;
(result+theta)->Uc = Uc_sub;
(result+theta)->Vc = Vc_sub;
}
}
// somewhere in your code where you want to use your function
struct boat_params values[11];
unsigned char i = 0;
submerged_values(/* parameters */, values);
for (; i <= 10; ++i) {
printf("V = %lf\nUc = %lf\nVc = %lf\n", values[i].V, values[i].Uc, values[i].Vc);
}
Try this, just add your logic to the loop and maths:
#include <stdio.h>
#include <stdlib.h>
#define ARRSIZE 100
typedef struct boat_params {
double V, Uc, Vc;
} Volume;
struct boat_params submerged_volume(double L1, double L2, double Lavg, double H, Volume *volumes[]) {
double theta;
int i = 0; /* only example, change as needed */
Volume *p;
for (theta = 0; theta <= 10; theta ++) {
p = malloc(sizeof(* p));
if (p == NULL) {
printf("malloc failed to allocate a new space");
exit(0);
}
p->V = 1; //V_sub;
p->Uc = 2; //Uc_sub;
p->Vc = 3; //Vc_sub;
volumes[i] = p;
i++;
}
}
int main () {
double L1, L2, Lavg, H;
L1 = 17.6;
L2 = 3;
Lavg = 4;
H = 4.5;
Volume *volumes[ARRSIZE];
submerged_volume(L1, L2, Lavg, H, volumes);
printf("V = %lf\nUc = %lf\nVc = %lf\n", volumes[0]->V, volumes[0]->Uc, volumes[0]->Vc); /* first element for example */
return 0;
}
If you don't know the size of the volumes array in advance, you should consider using linked list.