Stellarcomputer/star.c

/*
PC4181 MPhys Project - Stellar Computer

Calculating the properties of a star given a set of input numbers
Started: 11/10/2005
Last Modified: 09/01/2006

Matthew Dennison (matthew.dennison@student.manchester.ac.uk) & Mike Peel (http://www.mikepeel.net/)
*/

/*
Include the standard functions
*/
#include	<stdio.h>
#include	<math.h>
#include	<string.h>
#include	<stdlib.h>

/*
Set up the constants
N is the number of times we're doing the calculation
NUM_OPAL_I and NUM_OPAL_J are the number of rows and columns respectively (inc. logT and logR) of the opacity.txt file.
*/
#define N 100000
#define CONST_PI 3.14159
#define CONST_G  6.674e-11
#define CONST_K  1.381e-23
#define CONST_A  7.5657e-16
#define CONST_H  6.626e-34
#define CONST_C  299792458.0
#define MASS_H   1.673e-27
#define MASS_e   9.109e-31
#define NUM_OPAL_I 71
#define NUM_OPAL_J 20

/*
Set the input/output directory
*/
char directory[500] = "/star/Current/";

/*
Set up the arrays
*/
long double radius[N], err_radius[N];
long double mass[N];
long double temperature[N], err_temperature[N];
long double pressure[N], err_pressure[N];
long double density[N];
long double luminosity[N], err_luminosity[N];
long double opacity[N];
long double energy[N];
long double dm[N], min_dm, new_dm;
long double degenerate_pressure[N], fermi_pressure;
long double del_adiabatic[N], del_radiative[N];
long double mu;
long double X, Y, Z, A_Z;
long double opal_data[71][20];

/*
Set up the variables used throughout the application
The temp_ variables are used in the Runge-Kutta routines.
The set number variables are the coefficients for the various elements within the RUk w/ 5th order error checking
*/
long double temp_density[6],
			temp_radius[6],
			temp_pressure[6],
			temp_opacity[6],
			temp_temperature[6],
			temp_energy[6],
			temp_luminosity[6];
long double *ptr_density, *ptr_opacity, *ptr_energy;
long double a2 = 0.2,
			a3 = 3.0/10.0,
			a4 = 3.0/5.0,
			a5 = 1.0,
			a6 = 7.0/8.0;
long double b21 = 0.2,
			b31 = 3.0/40.0,
			b32 = 9.0/40.0,
			b41 = 3.0/10.0,
			b42 = -9.0/10.0,
			b43 = 6.0/5.0,
			b51 = -11.0/54.0,
			b52 = 2.5,
			b53 = -70.0/27.0,
			b54 = 35.0/27.0,
			b61 = 1631.0/55296.0,
			b62 = 175.0/512.0,
			b63 = 575.0/13824.0,
			b64 = 44275.0/110592.0,
			b65 = 253.0/4096.0;
long double c1 = 37.0/378.0,
			c2 = 0.0,
			c3 = 250.0/621.0,
			c4 = 125.0/594.0,
			c5 = 0.0,
			c6 = 512.0/1771.0;
long double C1 = 2825.0/27648.0,
			C2 = 0.0,
			C3 = 18575.0/48384.0,
			C4 = 13525.0/55296.0,
			C5 = 277.0/14336.0,
			C6 = 0.25;
long double dP[6], dT[6], dR[6], dL[6], dM[6];
long double P1, T1, R1, L1;
// error is used to check the numerical error and change dm
long double error = 1e-8;
long double old_temperature, old_pressure;
int direction, loop, i, i_min, i_max, k, j;
// accuracy determines the number of decimal places the program outputs.
int accuracy = 12;
FILE *input_file, *output_file, *opacity_file;
char filename[100];

/*
Work out the radiative constant for later
*/
long double radiative_constant = (3.0 / (16.0 * CONST_PI * CONST_A * CONST_C * CONST_G));

/*
Get the functions
*/
#include	"functions.h"

/*
This is the main program
*/
int main()
{
	/*
	Get the input data from input.txt
	*/
	sprintf(filename, "%sinput.txt", directory);
	input_file = fopen(filename, "r");
	if (!input_file)
	{
		printf("Failed to open %sinput.txt \n", directory);
		return 1;
	};
	fscanf(input_file, "Radius: %Lf\n", &radius[0]);
	fscanf(input_file, "Mass: %Lf\n", &mass[0]);
	fscanf(input_file, "Surface Temperature: %Lf\n", &temperature[0]);
	fscanf(input_file, "Luminosity: %Lf\n", &luminosity[0]);
	fscanf(input_file, "Core Temperature: %Lf\n", &temperature[N-1]);
	fscanf(input_file, "Core Pressure: %Lf\n", &pressure[N-1]);
	fscanf(input_file, "X: %Lf\n", &X);
	fscanf(input_file, "Y: %Lf\n", &Y);
	fscanf(input_file, "Z: %Lf\n", &Z);
	fscanf(input_file, "A_Z: %Lf\n", &A_Z);
	
	fclose(input_file);


	/*
	Get the OPAL data
	*/
	sprintf(filename, "%sopacity.txt", directory);
	opacity_file = fopen(filename, "r");
	if (!opacity_file)
	{
		printf("Failed to open %sopacity.txt \n", directory);
		return 1;
	};
	// For each column in the csv file...
	for (i = 0; i < NUM_OPAL_I; i++)
	{
		// For each row in the csv file...
		for (j = 0; j < NUM_OPAL_J; j++)
		{
			fscanf(opacity_file, "%Lf", &opal_data[i][j]);
			/*
			For debugging...
			printf("%Lf", opal_data[i][j]);
			*/
		};
		/*
		For debugging...
		printf("\n");
		*/
	};
	fclose(opacity_file);
	

	/*
	Set a minimum mass to be 100x smaller than that defined by N - that's the minimum we should need to use for accuracy of around 1e-8.
	*/
	min_dm = mass[0] / (100 * N);

	// Calculate mu
	mu = calc_mu(X, Y, Z, A_Z);

	/*
	Set the remaining boundary conditions at the surface and the centre.
	Pressure uses the surface pressure equation. See section 3.2 of the report for details. 1.85e-14 is kappa_3.
	*/
	dm[1] = min_dm;
	pressure[0] = pow(((CONST_G * mass[0] * pow(CONST_K, 0.5)) / (pow(radius[0], 2.0) * 1.85e-14 * pow(temperature[0], 3.5) * pow(mu, 0.5))), 0.66);
	density[0] = calc_density(X, temperature[0], pressure[0]);
	opacity[0] = 1.85e-14 * pow(density[0], 0.5) * pow(temperature[0], 4.0);

	/*
	Get the values for the core.
	Temperature and pressure have been defined when input.txt is read.
	*/
	dm[N-1] = min_dm;
	mass[N-1] = 0.0;
	luminosity[N-1] = 0.0;
	radius[N-1] = 0.0;
	density[N-1] = calc_density(X, temperature[N-1], pressure[N-1]);
	opacity[N-1] = calc_opacity(X, density[N-1], temperature[N-1]);
	energy[N-1] = calc_energy_generation(temperature[N-1], density[N-1], X, Y);

	/*
	Get the values for the second innermost shell.
	See section 3.1 of the report for details.
	*/
	dm[N-2] = min_dm;
	mass[N-2] = dm[N-1];
	radius[N-2] = pow(((3.0 * mass[N-2]) / (4.0 * CONST_PI * density[N-1])), 1.0 / 3.0);
	luminosity[N-2] = energy[N-1] * mass[N-2];
	pressure[N-2] = pressure[N-1] - 0.5 * pow((4.0 * CONST_PI / 3.0), (1.0 / 3.0)) * CONST_G * pow(density[N-1], (4.0 / 3.0)) * pow(mass[N-2], (2.0 / 3.0));
	del_radiative[N-2] = radiative_constant * opacity[N-1] * energy[N-1] * pressure[N-1] * pow(temperature[N-1], -4.0);
	temperature[N-2] = temperature[N-1] - (3.0 * opacity[N-1] * luminosity[N-2] * mass[N-2])/(256.0 * CONST_PI * CONST_PI * 5.67e-8 * pow(radius[N-2],4.0) * pow(temperature[N-1], 3.0));
	density[N-2] = calc_density(X, temperature[N-2], pressure[N-2]);
	opacity[N-2] = calc_opacity(X, density[N-2], temperature[N-2]);
	energy[N-2] = calc_energy_generation(temperature[N-2], density[N-2], X, Y);
	
	dm[N-3] = min_dm;

	for (loop = 0; loop < 2; loop++)
	{
		if (loop == 1)
		{
			/*
			This is the loop starting at the core, and working outwards.
			Direction is negative as i is decreasing - i=N is the center.
			*/
			direction = -1;
			i_min = N - 3;
			i_max = 0.5 * N;
		}
		else
		{
			/*
			This is the loop starting on the surface, and working inwards
			Direction is positive as i is increasing - i=1 is the surface.
			*/
			direction = +1;
			i_min = 1;
			i_max = 0.5 * N;
		};
		for (i = i_min; (loop == 0) ? i < i_max : i > i_max; i += direction)
		{
		
			/*
			Mass
			*/
			mass[i] = mass[i - direction] - direction * dm[i];
						
			/*
			First Runge-Kutta loop
			*/
			dP[0] = 0.0;
			dT[0] = 0.0;
			dR[0] = 0.0;
			dL[0] = 0.0;
			dM[0] = 0.0;
			ptr_density = &density[i - direction];
			ptr_energy = &energy[i - direction];
			ptr_opacity = &opacity[i - direction];
			old_temperature = (temperature[i - direction * 2] + temperature[i - direction]) * b21;
			old_pressure = (pressure[i - direction * 2] + pressure[i - direction]) * b21;
			rungekutta(direction, i, 0);

			/*
			Second Runge-Kutta loop
			*/
			dP[1] = temp_pressure[0] * b21;
			dT[1] = temp_temperature[0] * b21;
			dR[1] = temp_radius[0] * b21;
			dL[1] = temp_luminosity[0] * b21;
			dM[1] = dm[i] * a2;
			ptr_density = &temp_density[0];
			ptr_energy = &temp_energy[0];
			ptr_opacity = &temp_opacity[0];
			old_temperature = ((temperature[i - direction * 2] + temperature[i - direction]) * b21);
			old_pressure = ((pressure[i - direction * 2] + pressure[i - direction]) * b21);
			rungekutta(direction, i, 1);
			
			/*
			Third Runge-Kutta loop
			*/
			dP[2] = (temp_pressure[0] * b31) + (temp_pressure[1] * b32);
			dT[2] = (temp_temperature[0] * b31) + (temp_temperature[1] * b32);
			dR[2] = (temp_radius[0] * b31) + (temp_radius[1] * b32);
			dL[2] = (temp_luminosity[0] * b31) + (temp_luminosity[1] * b32);
			dM[2] = dm[i] * a3;
			ptr_density = &temp_density[1];
			ptr_energy = &temp_energy[1];
			ptr_opacity = &temp_opacity[1];
			old_temperature = (temperature[i - direction * 2] + temperature[i - direction]) / 2;
			old_pressure = (pressure[i - direction * 2] + pressure[i - direction]) / 2;
			rungekutta(direction, i, 2);

			/*
			Fourth Runge-Kutta loop
			*/
			dP[3] = (temp_pressure[0] * b41) + (temp_pressure[1] * b42) + (temp_pressure[2] * b43);
			dT[3] = (temp_temperature[0] * b41) + (temp_temperature[1] * b42) + (temp_temperature[2] * b43);
			dR[3] = (temp_radius[0] * b41) + (temp_radius[1] * b42) + (temp_radius[2] * b43);
			dL[3] = (temp_luminosity[0] * b41) + (temp_luminosity[1] * b42) + (temp_luminosity[2] * b43);
			dM[3] = dm[i] * a4;
			ptr_density = &temp_density[2];
			ptr_energy = &temp_energy[2];
			ptr_opacity = &temp_opacity[2];
			old_temperature = temperature[i - direction];
			old_pressure = pressure[i - direction];
			rungekutta(direction, i, 3);

			/*
			Fifth Runge-Kutta loop
			*/
			dP[4] = (temp_pressure[0] * b51) + (temp_pressure[1] * b52) + (temp_pressure[2] * b53) + (temp_pressure[3] * b54);
			dT[4] = (temp_temperature[0] * 51) + (temp_temperature[1] * b52) + (temp_temperature[2] * b53) + (temp_temperature[3] * b54);
			dR[4] = (temp_radius[0] * b51) + (temp_radius[1] * b52) + (temp_radius[2] * b53) + (temp_radius[3] * b54);
			dL[4] = (temp_luminosity[0] * b51) + (temp_luminosity[1] * b52) + (temp_luminosity[2] * b53) + (temp_luminosity[3] * b54);
			dM[4] = dm[i] * a5;
			ptr_density = &temp_density[3];
			ptr_energy = &temp_energy[3];
			ptr_opacity = &temp_opacity[3];
			old_temperature = temperature[i - direction];
			old_pressure = pressure[i - direction];
			rungekutta(direction, i, 4);

			/*
			Sixth Runge-Kutta loop
			*/
			dP[5] = (temp_pressure[0] * b61) + (temp_pressure[1] * b62) + (temp_pressure[2] * b63) + (temp_pressure[3] * b64) + (temp_pressure[4] * b65);
			dT[5] = (temp_temperature[0] * 61) + (temp_temperature[1] * b62) + (temp_temperature[2] * b63) + (temp_temperature[3] * b64) + (temp_temperature[4] * b65);
			dR[5] = (temp_radius[0] * b61) + (temp_radius[1] * b62) + (temp_radius[2] * b63) + (temp_radius[3] * b64) + (temp_radius[4] * b65);
			dL[5] = (temp_luminosity[0] * b61) + (temp_luminosity[1] * b62) + (temp_luminosity[2] * b63) + (temp_luminosity[3] * b64) + (temp_luminosity[4] * b65);
			dM[5] = dm[i] * a6;
			ptr_density = &temp_density[4];
			ptr_energy = &temp_energy[4];
			ptr_opacity = &temp_opacity[4];
			old_temperature = temperature[i - direction];
			old_pressure = pressure[i - direction];
			rungekutta(direction, i, 5);

			/*
			Finally, compute the values.
			*/
			radius[i] = radius[i - direction] - direction * ((temp_radius[0] * c1) + (temp_radius[1] * c2) + (temp_radius[2] * c3) + (temp_radius[3] * c4) + (temp_radius[4] * c5) + (temp_radius[5] * c6));
			pressure[i] = pressure[i - direction] + direction * ((temp_pressure[0] * c1) + (temp_pressure[1] * c2) + (temp_pressure[2] * c3) + (temp_pressure[3] * c4) + (temp_pressure[4] * c5) + (temp_pressure[5] * c6));
			temperature[i] = temperature[i - direction] + direction * ((temp_temperature[0] * c1) + (temp_temperature[1] * c2) + (temp_temperature[2] * c3) + (temp_temperature[3] * c4) + (temp_temperature[4] * c5) + (temp_temperature[5] * c6));
			luminosity[i] = luminosity[i - direction] - direction * ((temp_luminosity[0] * c1) + (temp_luminosity[1] * c2) + (temp_luminosity[2] * c3) + (temp_luminosity[3] * c4) + (temp_luminosity[4] * c5) + (temp_luminosity[5] * c6));

			err_radius[i] = sqrt(pow(radius [i] - (radius[i - direction] - direction *((temp_radius[0] * C1) + (temp_radius[1] * C2) + (temp_radius[2] * C3) + (temp_radius[3] * C4) + (temp_radius[4] * C5) + (temp_radius[5] * C6))), 2.0));
			err_pressure[i] = sqrt(pow(pressure[i] - (pressure[i - direction] + direction *((temp_pressure[0] * C1) + (temp_pressure[1] * C2) + (temp_pressure[2] * C3) + (temp_pressure[3] * C4) + (temp_pressure[4] * C5) + (temp_pressure[5] * C6))), 2.0));
			err_temperature[i] = sqrt(pow(temperature[i] - (temperature[i - direction] + direction *((temp_temperature[0] * C1) + (temp_temperature[1] * C2) + (temp_temperature[2] * C3) + (temp_temperature[3] * C4) + (temp_temperature[4] * C5) + (temp_temperature[5] * C6))), 2.0));
			err_luminosity[i] = sqrt(pow(luminosity[i] - (luminosity[i - direction] - direction *((temp_luminosity[0] * C1) + (temp_luminosity[1] * C2) + (temp_luminosity[2] * C3) + (temp_luminosity[3] * C4) + (temp_luminosity[4] * C5) + (temp_luminosity[5] * C6))), 2.0));

			L1 = pow(((error * luminosity[i]) / err_luminosity[i]), 0.2);
			T1 = pow(((error * temperature[i]) / err_temperature[i]), 0.2);
			R1 = pow(((error * radius[i]) / err_radius[i]), 0.2);
			P1 = pow(((error * pressure[i]) / err_pressure[i]), 0.2);

			if (L1 < T1 && L1 < R1 && L1 < P1)
			{
				new_dm = dm[i] * L1;
			}
			else if	(T1 > R1 && T1 > P1)
			{
				new_dm = dm[i] * T1;
			}
			else if	(R1 > P1)
			{
				new_dm = dm[i] * R1;
			}
			else
			{
				new_dm = dm[i] * P1;
			};
			
			// Check and see which one is the greater - new_dm or min_dm - and use that one.
			if (new_dm < min_dm)
			{
				dm[i + direction] = min_dm;
			}
			else
			{
				dm[i + direction] = new_dm;
			};
			
			density[i] = calc_density(X, temperature[i], pressure[i]);
			opacity[i] = calc_opacity(X, density[i], temperature[i]);
			energy[i] = calc_energy_generation(temperature[i], density[i], X, Y);
			
			/*
			Calculate the degeneracy pressure.
			*/
			fermi_pressure = pow((3.0 * density[i]) / (8.0 * CONST_PI * X * MASS_H), 1.0/3.0) * CONST_H;
			if (fermi_pressure < MASS_e * 3.0e8)
			{	
				degenerate_pressure[i] = (pow(CONST_H, 2.0) * pow(3.0 / CONST_PI, 2.0 / 3.0) * pow(density[i] / 2.0, 5.0 / 3.0)) / ( 20.0 * MASS_e * pow(MASS_H, 5.0 / 3.0));
			}
			else
			{
				degenerate_pressure[i] = (CONST_H * 3.0e8 * pow(3.0 / CONST_PI, 1.0 / 3.0) * pow(density[i] / 2.0, 4.0 / 3.0)) / (8.0 * pow (MASS_H, 4.0 / 3.0));
			};

			/*
			Are we there yet? ("there" being half the mass of the star)
			Alternatively; have things gone to nan? (Could check more than just temperature for nan, but as all the variables are interlinked there's not much point doing so)
			*/
			if (loop == 1 && (mass[i] > (mass[0] / 2.0) || isnan(temperature[i]) || temperature[i] == 0.0))
			{
				j = i;
				break;
			}
			else if (loop != 1 && (mass[i] < (mass[0] / 2.0) || isnan(temperature[i]) || temperature[i] == 0.0))
			{
				k = i + 1;
				break;
			};
			
		}; // End calculation loop
	}; // End big loop

	/*
	Create the output file
	*/
	sprintf(filename, "%sOutput.csv", directory);
	output_file = fopen(filename, "w");
	if (!output_file)
	{
		printf("Failed to open %soutput.csv \n", directory);
		return 1;
	};
	fprintf(output_file, "Number,Mass,Density,Radius,Radius Error,Pressure,Pressure Error,Degeneracy,Opacity,Temperature,Temperature Error,del_radiative,del_adiabatic,Energy,Luminosity,Luminosity Error\n");

	/*
	Output the calculated variables
	*/
	for (loop = 0; loop < 2; loop++)
	{
		if (loop == 0)
		{
			/*
			This is the loop starting on the surface, and working inwards
			Direction is positive as i is increasing - i=1 is the surface.
			*/
			direction = +1.0;
			i_min = 0;
			i_max = k;
		}
		else
		{
			/*
			This is the loop starting at the core, and working outwards.
			Direction is negative as i is decreasing - i=N is the center.
			*/
			direction = +1.0;
			i_min = j;
			i_max = N;
		};
		for (i = i_min; i < i_max; i = i + direction)
		{
			if (mass[i] != 0.0 || i > N-3)
			{
				fprintf(output_file, "%d,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le,%.*Le\n", i, accuracy, mass[i], accuracy, density[i], accuracy, radius[i],  accuracy, err_radius[i], accuracy, pressure[i], accuracy, err_pressure[i],  accuracy, degenerate_pressure[i], accuracy, opacity[i], accuracy, temperature[i],  accuracy, err_temperature[i], accuracy, del_radiative[i], accuracy, del_adiabatic[i], accuracy, energy[i], accuracy, luminosity[i], accuracy, err_luminosity[i]);
			};
		};
	};

	fclose(output_file);

	/*
	All is done, so end the program.
	*/
	printf("\n%d positions calculated (%d inwards, %d outwards)\n", (N - j) + k, k, (N - j));
	return 0;
}

- Permanent link -


Like my photographs? Please donate to my photography fund: