Beispiel #1
0
//Evaluate any routines prior to solving the system
int SCOPSOWL_preprocesses(SCOPSOWL_DATA *owl_dat)
{
	int success = 0;
	
	//Establish boundary conditions before simulation starts
	owl_dat->magpie_dat.sys_dat.T = owl_dat->gas_temperature;
	owl_dat->magpie_dat.sys_dat.PT = owl_dat->total_pressure;
	for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
	{
		if (owl_dat->SurfDiff == true && owl_dat->Heterogeneous == true)
		{
			for (int l=0; l<owl_dat->finch_dat[i].LN; l++)
			{
				double p_out = owl_dat->y[i]*owl_dat->total_pressure;
				if (p_out >= owl_dat->param_dat[i].ref_pressure) owl_dat->param_dat[i].ref_pressure = p_out;
				owl_dat->skua_dat[l].param_dat[i].ref_diffusion = D_inf(owl_dat->param_dat[i].ref_diffusion, owl_dat->param_dat[i].ref_temperature, owl_dat->param_dat[i].affinity, owl_dat->param_dat[i].ref_pressure, owl_dat->gas_temperature);
				owl_dat->skua_dat[l].param_dat[i].activation_energy = owl_dat->param_dat[i].activation_energy;
				owl_dat->skua_dat[l].param_dat[i].ref_temperature = 0.0;
				owl_dat->skua_dat[l].param_dat[i].affinity = 0.0;
			}
		}
		owl_dat->finch_dat[i].uo = Cstd(owl_dat->magpie_dat.sys_dat.PT*owl_dat->y[i], owl_dat->magpie_dat.sys_dat.T);
		owl_dat->finch_dat[i].Ro = (*owl_dat->eval_retard) (i,-1,owl_dat);
		if (owl_dat->finch_dat[i].Ro < 0.0)
		{
			mError(simulation_fail);
			return -1;
		}
		owl_dat->finch_dat[i].Do = (*owl_dat->eval_diff) (i,-1,owl_dat);
		owl_dat->finch_dat[i].kfnp1 = (*owl_dat->eval_kf) (i,owl_dat);
	}
	
	return success;
}
Real
MAGPIE_Perturbation::computeValue()
{
	MAGPIE_DATA magpie_copy;
	magpie_copy = _magpie_dat[_qp];
	
	
	//Check for adsorption
	if (_magpie_dat[_qp].gsta_dat[_index].qmax > 0.0)
	{
		//perturn the copy's _index y
		double pi = _magpie_dat[_qp].gpast_dat[_index].y * _magpie_dat[_qp].sys_dat.PT;
		double ci = Cstd(pi,_magpie_dat[_qp].sys_dat.T) + sqrt(DBL_EPSILON);
		double yi = Pstd(ci,_magpie_dat[_qp].sys_dat.T) / _magpie_dat[_qp].sys_dat.PT;
		magpie_copy.gpast_dat[_index].y = yi;
	
	
		int success = 0;
		success = MAGPIE( (void *)&magpie_copy );
		if (success < 0 || success > 3) {mError(simulation_fail);}
		else success = 0;
		
		return magpie_copy.gpast_dat[_index].q;
		
		//This override was used to demonstrate that we can do perturbation with kinetics
		//double k = magpie_copy.gpast_dat[_index].q * 0.008;
		//double qe = magpie_copy.gpast_dat[_index].q;
		//return (_u_old[_qp] + (_dt*k*qe))/(1.0+(_dt*k));
	}
	else
	{
		return 0.0;
	}
}
Beispiel #3
0
//Set up the ICs for the problem
int set_SCOPSOWL_ICs(SCOPSOWL_DATA *owl_dat)
{
	int success = 0;
	
	//Print file header
	if (owl_dat->Print2File == true)
		print2file_SCOPSOWL_header(owl_dat);
	
	owl_dat->magpie_dat.sys_dat.PT = owl_dat->total_pressure;
	owl_dat->magpie_dat.sys_dat.T = owl_dat->gas_temperature;
	if (owl_dat->SurfDiff == true && owl_dat->Heterogeneous == true)
	{
		//Call SKUA ICs to establish SCOPSOWL ICs
		for (int l=0; l<owl_dat->finch_dat[0].LN; l++)
		{
			owl_dat->skua_dat[l].total_steps = 0;
			owl_dat->skua_dat[l].magpie_dat.sys_dat.T = owl_dat->gas_temperature;
			owl_dat->skua_dat[l].magpie_dat.sys_dat.PT = owl_dat->total_pressure;
			for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
			{
				owl_dat->skua_dat[l].param_dat[i].ref_diffusion = D_inf(owl_dat->param_dat[i].ref_diffusion, owl_dat->param_dat[i].ref_temperature, owl_dat->param_dat[i].affinity, owl_dat->y[i]*owl_dat->total_pressure, owl_dat->gas_temperature);
				owl_dat->skua_dat[l].param_dat[i].activation_energy = owl_dat->param_dat[i].activation_energy;
				owl_dat->skua_dat[l].param_dat[i].ref_temperature = 0.0;
				owl_dat->skua_dat[l].param_dat[i].affinity = 0.0;
				
				owl_dat->skua_dat[l].param_dat[i].xIC = owl_dat->param_dat[i].xIC;
			}
			success = set_SKUA_ICs(&owl_dat->skua_dat[l]);
			if (success != 0) {mError(simulation_fail); return -1;}
			owl_dat->total_steps = owl_dat->total_steps + owl_dat->skua_dat[l].total_steps;
			
			//Loop for species
			for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
			{
				owl_dat->param_dat[i].qAvg.edit(l, 0, owl_dat->skua_dat[l].finch_dat[i].uAvg);
				owl_dat->param_dat[i].Qst.edit(l, 0, owl_dat->skua_dat[l].param_dat[i].Qstn);
				owl_dat->param_dat[i].qAvg_old.edit(l, 0, owl_dat->param_dat[i].qAvg(l,0));
				owl_dat->param_dat[i].Qst_old.edit(l, 0, owl_dat->param_dat[i].Qst(l,0));
				owl_dat->magpie_dat.gpast_dat[i].y = owl_dat->skua_dat[l].magpie_dat.gpast_dat[i].y;
			}
		}
		
		//Outer species loop
		double yad_sum = 0.0;
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			//Now form averages and totals
			owl_dat->param_dat[i].Qsto = owl_dat->param_dat[i].Qst(0,0);
			owl_dat->param_dat[i].qo = owl_dat->param_dat[i].qAvg(owl_dat->finch_dat[i].LN-1,0);
			owl_dat->param_dat[i].qIntegralAvg = owl_dat->param_dat[i].qAvg.sphericalAvg(owl_dat->finch_dat[i].L,owl_dat->finch_dat[i].dz,owl_dat->param_dat[i].qo,owl_dat->DirichletBC);
			owl_dat->param_dat[i].QstAvg = owl_dat->param_dat[i].Qst.sphericalAvg(owl_dat->finch_dat[i].L, owl_dat->finch_dat[i].dz,owl_dat->param_dat[i].Qsto,owl_dat->DirichletBC);
			owl_dat->param_dat[i].QstAvg_old = owl_dat->param_dat[i].QstAvg;
			owl_dat->param_dat[i].qIntegralAvg_old = owl_dat->param_dat[i].qIntegralAvg;
			
			//SCOPSOWL ICs
			owl_dat->finch_dat[i].Sn.ConstantICFill(0.0);
			owl_dat->finch_dat[i].Snp1.ConstantICFill(0.0);
			owl_dat->finch_dat[i].vn.ConstantICFill(0.0);
			owl_dat->finch_dat[i].vnp1.ConstantICFill(0.0);
			owl_dat->finch_dat[i].kn.ConstantICFill(0.0);
			owl_dat->finch_dat[i].knp1.ConstantICFill(0.0);
			owl_dat->finch_dat[i].ko = 0.0;
			owl_dat->finch_dat[i].vo = 0.0;
			
			owl_dat->finch_dat[i].kfn = (*owl_dat->eval_kf) (i,owl_dat);
			owl_dat->finch_dat[i].kfnp1 = owl_dat->finch_dat[i].kfn;
			if (owl_dat->finch_dat[i].kfn < 0.0)
			{
				mError(simulation_fail);
				return -1;
			}
			
			if (owl_dat->param_dat[i].Adsorbable == true)
			{
				owl_dat->finch_dat[i].un.ConstantICFill(Cstd(owl_dat->magpie_dat.sys_dat.PT*owl_dat->magpie_dat.gpast_dat[i].y,owl_dat->magpie_dat.sys_dat.T));
			}
			
			if (owl_dat->param_dat[i].Adsorbable == true)
				yad_sum = yad_sum + owl_dat->magpie_dat.gpast_dat[i].y;
		}
		
		//Need to re-adjust the initial conditions for non-adsorbing species
		double rat_frac = 0.0;
		int first = 0;
		bool changed = false;
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			if (owl_dat->param_dat[i].Adsorbable == false)
			{
				if (changed == false)
				{
					first = i; changed = true;
					rat_frac = rat_frac + (owl_dat->y[i] / owl_dat->y[first]);
				}
				else
				{
					rat_frac = rat_frac + (owl_dat->y[i] / owl_dat->y[first]);
				}
			}
		}
		double yFirst = (1.0 - yad_sum) / rat_frac;
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			double yi = 0.0;
			if (owl_dat->param_dat[i].Adsorbable == false)
			{
				yi = yFirst * (owl_dat->y[i] / owl_dat->y[first]);
				owl_dat->finch_dat[i].un.ConstantICFill(Cstd(owl_dat->magpie_dat.sys_dat.PT*yi,owl_dat->magpie_dat.sys_dat.T));
			}
			owl_dat->finch_dat[i].uo = owl_dat->finch_dat[i].un(0,0);
			owl_dat->finch_dat[i].unp1 = owl_dat->finch_dat[i].un;
			owl_dat->finch_dat[i].unm1 = owl_dat->finch_dat[i].un;
			
			//Form Totals
			success = uTotal(&owl_dat->finch_dat[i]);
			if (success != 0) {mError(simulation_fail); return -1;}
			
			//Form Averages
			success = uAverage(&owl_dat->finch_dat[i]);
			if (success != 0) {mError(simulation_fail); return -1;}
		}
		
		//Print out ICs
		if (owl_dat->Print2File == true)
			print2file_SCOPSOWL_result_old(owl_dat);
		
		//Call parameter functions
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			for (int l=0; l<owl_dat->finch_dat[i].LN; l++)
			{
				double p_out = owl_dat->y[i]*owl_dat->total_pressure;
				double p_in = Pstd(owl_dat->finch_dat[i].un(l,0), owl_dat->gas_temperature);
				if (p_out >= p_in)	owl_dat->param_dat[i].ref_pressure = p_out;
				else owl_dat->param_dat[i].ref_pressure = p_in;
				owl_dat->skua_dat[l].param_dat[i].ref_diffusion = D_inf(owl_dat->param_dat[i].ref_diffusion, owl_dat->param_dat[i].ref_temperature, owl_dat->param_dat[i].affinity, owl_dat->param_dat[i].ref_pressure, owl_dat->gas_temperature);
				owl_dat->skua_dat[l].param_dat[i].activation_energy = owl_dat->param_dat[i].activation_energy;
				owl_dat->skua_dat[l].param_dat[i].ref_temperature = 0.0;
				owl_dat->skua_dat[l].param_dat[i].affinity = 0.0;
				
				//Now Recall skua parameter functions to apply corrections
				owl_dat->skua_dat[l].finch_dat[i].Do = (*owl_dat->skua_dat[l].eval_diff) (i, -1, (void *)&owl_dat->skua_dat[l]);
				owl_dat->skua_dat[l].finch_dat[i].Dn.ConstantICFill(owl_dat->skua_dat[l].finch_dat[i].Do);
				owl_dat->skua_dat[l].finch_dat[i].Dnp1.ConstantICFill(owl_dat->skua_dat[l].finch_dat[i].Do);
				
				owl_dat->finch_dat[i].Rn.edit(l, 0, (*owl_dat->eval_retard) (i,l,owl_dat));
				owl_dat->finch_dat[i].Dn.edit(l, 0, (*owl_dat->eval_diff) (i,l,owl_dat));
			}
			owl_dat->finch_dat[i].Ro = (*owl_dat->eval_retard) (i,-1,owl_dat);
			owl_dat->finch_dat[i].Do = (*owl_dat->eval_diff) (i,-1,owl_dat);
			owl_dat->finch_dat[i].Rnp1 = owl_dat->finch_dat[i].Rn;
			owl_dat->finch_dat[i].Dnp1 = owl_dat->finch_dat[i].Dn;
			
			//Update averages and totals
			owl_dat->finch_dat[i].uT_old = owl_dat->finch_dat[i].uT;
			owl_dat->finch_dat[i].uAvg_old = owl_dat->finch_dat[i].uAvg;
		}
		
	}
	else
	{
		//Call MAGPIE to establish SCOPSOWL ICs
		owl_dat->magpie_dat.sys_dat.Recover = true;
		owl_dat->magpie_dat.sys_dat.Carrier = false;
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			if (owl_dat->param_dat[i].Adsorbable == false)
			{
				owl_dat->magpie_dat.sys_dat.Carrier = true;
				break;
			}
		}
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
			owl_dat->magpie_dat.gpast_dat[i].x = owl_dat->param_dat[i].xIC;
		
		//Temperature, pressure, total adsorption, and x's are set: Call MAGPIE
		success = MAGPIE((void *)&owl_dat->magpie_dat);
		if (success < 0 || success > 3) {mError(simulation_fail); return -1;}
		else success = 0;
		owl_dat->total_steps = owl_dat->total_steps + owl_dat->magpie_dat.sys_dat.total_eval;
		
		//Reset the MAGPIE parameters before continuing
		owl_dat->magpie_dat.sys_dat.Recover = false;
		owl_dat->magpie_dat.sys_dat.Carrier = false;
		
		//The GPAST structure of MAGPIE now holds the y's which establish the SCOPSOWL ICs
		double yad_sum = 0.0;
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			owl_dat->param_dat[i].qAvg.ConstantICFill(owl_dat->magpie_dat.sys_dat.qT * owl_dat->magpie_dat.gpast_dat[i].x);
			owl_dat->param_dat[i].qAvg_old = owl_dat->param_dat[i].qAvg;
			owl_dat->param_dat[i].qo = owl_dat->magpie_dat.sys_dat.qT * owl_dat->magpie_dat.gpast_dat[i].x;
			
			owl_dat->finch_dat[i].Sn.ConstantICFill(0.0);
			owl_dat->finch_dat[i].Snp1.ConstantICFill(0.0);
			owl_dat->finch_dat[i].vn.ConstantICFill(0.0);
			owl_dat->finch_dat[i].vnp1.ConstantICFill(0.0);
			owl_dat->finch_dat[i].kn.ConstantICFill(0.0);
			owl_dat->finch_dat[i].knp1.ConstantICFill(0.0);
			owl_dat->finch_dat[i].ko = 0.0;
			owl_dat->finch_dat[i].vo = 0.0;
			
			owl_dat->finch_dat[i].kfn = (*owl_dat->eval_kf) (i,owl_dat);
			owl_dat->finch_dat[i].kfnp1 = owl_dat->finch_dat[i].kfn;
			if (owl_dat->finch_dat[i].kfn < 0.0)
			{
				mError(simulation_fail);
				return -1;
			}
			
			if (owl_dat->param_dat[i].Adsorbable == true)
			{
				owl_dat->finch_dat[i].un.ConstantICFill(Cstd(owl_dat->magpie_dat.sys_dat.PT*owl_dat->magpie_dat.gpast_dat[i].y,owl_dat->magpie_dat.sys_dat.T));
			}
			
			if (owl_dat->param_dat[i].Adsorbable == true)
				yad_sum = yad_sum + owl_dat->magpie_dat.gpast_dat[i].y;
		}
		
		//Need to re-adjust the initial conditions for non-adsorbing species
		double rat_frac = 0.0;
		int first = 0;
		bool changed = false;
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			if (owl_dat->param_dat[i].Adsorbable == false)
			{
				if (changed == false)
				{
					first = i; changed = true;
					rat_frac = rat_frac + (owl_dat->y[i] / owl_dat->y[first]);
				}
				else
				{
					rat_frac = rat_frac + (owl_dat->y[i] / owl_dat->y[first]);
				}
			}
		}
		double yFirst = (1.0 - yad_sum) / rat_frac;
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			double yi = 0.0;
			if (owl_dat->param_dat[i].Adsorbable == false)
			{
				yi = yFirst * (owl_dat->y[i] / owl_dat->y[first]);
				owl_dat->finch_dat[i].un.ConstantICFill(Cstd(owl_dat->magpie_dat.sys_dat.PT*yi,owl_dat->magpie_dat.sys_dat.T));
			}
			owl_dat->finch_dat[i].uo = owl_dat->finch_dat[i].un(0,0);
			owl_dat->finch_dat[i].unp1 = owl_dat->finch_dat[i].un;
			owl_dat->finch_dat[i].unm1 = owl_dat->finch_dat[i].un;
			
			//Form Totals
			success = uTotal(&owl_dat->finch_dat[i]);
			if (success != 0) {mError(simulation_fail); return -1;}
			
			//Form Averages
			success = uAverage(&owl_dat->finch_dat[i]);
			if (success != 0) {mError(simulation_fail); return -1;}
		}
		
		//Use established info to call the retardation and diffusion functions as well as heats of adsorption
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			if (owl_dat->param_dat[i].Adsorbable == true)
			{
				owl_dat->param_dat[i].Qst.ConstantICFill(Qst(Pstd(owl_dat->finch_dat[i].un(0,0),owl_dat->magpie_dat.sys_dat.T), (void *)&owl_dat->magpie_dat, i));
			}
			else
			{
				owl_dat->param_dat[i].Qst.ConstantICFill(0.0);
			}
			owl_dat->param_dat[i].Qst_old = owl_dat->param_dat[i].Qst;
			owl_dat->param_dat[i].Qsto = owl_dat->param_dat[i].Qst(0,0);
			
			//Now form averages and totals
			owl_dat->param_dat[i].qIntegralAvg = owl_dat->param_dat[i].qAvg.sphericalAvg(owl_dat->finch_dat[i].L,owl_dat->finch_dat[i].dz,owl_dat->param_dat[i].qo,owl_dat->DirichletBC);
			owl_dat->param_dat[i].QstAvg = owl_dat->param_dat[i].Qst.sphericalAvg(owl_dat->finch_dat[i].L, owl_dat->finch_dat[i].dz,owl_dat->param_dat[i].Qsto,owl_dat->DirichletBC);
			
			//Update adsorption info
			owl_dat->param_dat[i].QstAvg_old = owl_dat->param_dat[i].QstAvg;
			owl_dat->param_dat[i].qIntegralAvg_old = owl_dat->param_dat[i].qIntegralAvg;
		}
		
		//Print out ICs
		if (owl_dat->Print2File == true)
			print2file_SCOPSOWL_result_old(owl_dat);
		
		//Call the parameter functions
		for (int i=0; i<owl_dat->magpie_dat.sys_dat.N; i++)
		{
			//Set the reference pressure
			for (int l=0; l<owl_dat->finch_dat[i].LN; l++)
			{
				double p_out = owl_dat->y[i]*owl_dat->total_pressure;
				double p_in = Pstd(owl_dat->finch_dat[i].un(l,0), owl_dat->gas_temperature);
				if (p_out >= p_in)	owl_dat->param_dat[i].ref_pressure = p_out;
				else owl_dat->param_dat[i].ref_pressure = p_in;
			}
			for (int l=0; l<owl_dat->finch_dat[i].LN; l++)
			{
				owl_dat->finch_dat[i].Rn.edit(l, 0, (*owl_dat->eval_retard) (i,l,owl_dat));
				owl_dat->finch_dat[i].Dn.edit(l, 0, (*owl_dat->eval_diff) (i,l,owl_dat));
			}
			owl_dat->finch_dat[i].Ro = (*owl_dat->eval_retard) (i,-1,owl_dat);
			owl_dat->finch_dat[i].Do = (*owl_dat->eval_diff) (i,-1,owl_dat);
			owl_dat->finch_dat[i].Rnp1 = owl_dat->finch_dat[i].Rn;
			owl_dat->finch_dat[i].Dnp1 = owl_dat->finch_dat[i].Dn;
			
			//Update averages and totals
			owl_dat->finch_dat[i].uT_old = owl_dat->finch_dat[i].uT;
			owl_dat->finch_dat[i].uAvg_old = owl_dat->finch_dat[i].uAvg;
		}
		
	}
	
	return success;
}
Beispiel #4
0
//Default Adsorption Strength function is based on input args
double default_adsorption(int i, int l, const void *user_data)
{
	SCOPSOWL_DATA *dat = (SCOPSOWL_DATA *) user_data;
	double dq_dc = 0.0;
	double eps = sqrt(DBL_EPSILON);
	double qEPS = 0.0;
	double uo = 0.0;
	int success = 0;
	
	//Check if any work needs to be done
	if (dat->param_dat[i].Adsorbable == false)
	{
		if (l < 0)
			dat->param_dat[i].dq_dco = 0.0;
		else
			dat->param_dat[i].dq_dc.edit(l, 0, 0.0);
		return 0.0;
	}
	
	//Boundary value of dq/dc
	if (l < 0)
	{
		//Based on MAGPIE simulations ONLY!
		uo = Cstd(dat->magpie_dat.sys_dat.PT*dat->y[i], dat->magpie_dat.sys_dat.T);
		
		//Perturb the ith concentration
		for (int j=0; j<dat->magpie_dat.sys_dat.N; j++)
		{
			if (j == i)
				dat->magpie_dat.gpast_dat[j].y = Pstd(uo+eps,dat->magpie_dat.sys_dat.T)/dat->magpie_dat.sys_dat.PT;
			else if (dat->param_dat[j].Adsorbable == true)
				dat->magpie_dat.gpast_dat[j].y = Pstd(uo,dat->magpie_dat.sys_dat.T)/dat->magpie_dat.sys_dat.PT;
			else
				dat->magpie_dat.gpast_dat[j].y = 0.0;
		}
		
		//Call MAGPIE to evaluate perturbed adsorption
		success = MAGPIE((void *)&dat->magpie_dat);
		if (success < 0 || success > 3) {mError(simulation_fail); return -1;}
		else success = 0;
		dat->total_steps = dat->total_steps + dat->magpie_dat.sys_dat.total_eval;
		
		//Store perturbed result in temp location
		qEPS = dat->magpie_dat.sys_dat.qT * dat->magpie_dat.gpast_dat[i].x;
		
		//Restore original info
		for (int j=0; j<dat->magpie_dat.sys_dat.N; j++)
		{
			if (dat->param_dat[j].Adsorbable == true)
				dat->magpie_dat.gpast_dat[j].y = dat->y[j];
			else
				dat->magpie_dat.gpast_dat[j].y = 0.0;
		}
		
		//Call MAGPIE to evaluate adsorption
		success = MAGPIE((void *)&dat->magpie_dat);
		if (success < 0 || success > 3) {mError(simulation_fail); return -1;}
		else success = 0;
		dat->total_steps = dat->total_steps + dat->magpie_dat.sys_dat.total_eval;
		
		//Store the solution
		if (dat->param_dat[i].Adsorbable == true)
		{
			dat->param_dat[i].qo = dat->magpie_dat.sys_dat.qT * dat->magpie_dat.gpast_dat[i].x;
			dat->param_dat[i].Qsto = Qst(Pstd(uo,dat->magpie_dat.sys_dat.T), (void *)&dat->magpie_dat, i);
		}
		else
		{
			dat->param_dat[i].qo = 0.0;
			dat->param_dat[i].Qsto = 0.0;
		}
		
		dq_dc = ( qEPS - dat->param_dat[i].qo ) / eps;
		dat->param_dat[i].dq_dco = dq_dc;
		
	}
	//Nodal values of dq/dc
	else
	{
		if (dat->SurfDiff == true && dat->Heterogeneous == true)
		{
			//Adjust interior pressure with perturbation
			double tempPT = 0.0;
			for (int j=0; j<dat->skua_dat[l].magpie_dat.sys_dat.N; j++)
			{
				if (i == j)
					tempPT = tempPT + Pstd(dat->finch_dat[j].unp1(l,0)+eps, dat->skua_dat[l].magpie_dat.sys_dat.T);
				else
					tempPT = tempPT + Pstd(dat->finch_dat[j].unp1(l,0), dat->skua_dat[l].magpie_dat.sys_dat.T);
			}
			dat->skua_dat[l].magpie_dat.sys_dat.PT = tempPT;
			
			//Perform SKUA simulation
			dat->skua_dat[l].finch_dat[i].dt = dat->finch_dat[i].dt;
			dat->skua_dat[l].finch_dat[i].dt_old = dat->finch_dat[i].dt_old;
			dat->skua_dat[l].finch_dat[i].t = dat->finch_dat[i].t;
			dat->skua_dat[l].finch_dat[i].t_old = dat->finch_dat[i].t_old;
			dat->skua_dat[l].t = dat->skua_dat[l].finch_dat[i].t;
			dat->skua_dat[l].t_old = dat->skua_dat[l].finch_dat[i].t_old;
			
			//Set up SKUA data
			dat->skua_dat[l].magpie_dat.sys_dat.T = dat->magpie_dat.sys_dat.T;
			for (int j=0; j<dat->skua_dat[l].magpie_dat.sys_dat.N; j++)
			{
				if (i == j)
					dat->skua_dat[l].y[j] = Pstd(dat->finch_dat[j].unp1(l,0)+eps,dat->skua_dat[l].magpie_dat.sys_dat.T)/dat->skua_dat[l].magpie_dat.sys_dat.PT;
				else
					dat->skua_dat[l].y[j] = Pstd(dat->finch_dat[j].unp1(l,0),dat->skua_dat[l].magpie_dat.sys_dat.T)/dat->skua_dat[l].magpie_dat.sys_dat.PT;
			}
			
			//Call SKUA executioner to evaluate adsorption
			dat->skua_dat[l].total_steps = 0;
			success = SKUA_Executioner(&dat->skua_dat[l]);
			if (success != 0) {mError(simulation_fail); return -1;}
			dat->total_steps = dat->total_steps + dat->skua_dat[l].total_steps;
			
			//Store the results temporarily
			qEPS = dat->skua_dat[l].finch_dat[i].uAvg;
			
			//Adjust again without perturbation
			tempPT = 0.0;
			for (int j=0; j<dat->skua_dat[l].magpie_dat.sys_dat.N; j++)
				tempPT = tempPT + Pstd(dat->finch_dat[j].unp1(l,0), dat->skua_dat[l].magpie_dat.sys_dat.T);
			dat->skua_dat[l].magpie_dat.sys_dat.PT = tempPT;
			
			//Undo perturbation
			for (int j=0; j<dat->magpie_dat.sys_dat.N; j++)
			{
				dat->skua_dat[l].y[j] = Pstd(dat->finch_dat[j].unp1(l,0),dat->magpie_dat.sys_dat.T)/dat->skua_dat[l].magpie_dat.sys_dat.PT;
			}
			
			//Call SKUA again to evaluate perturbation
			dat->skua_dat[l].total_steps = 0;
			success = SKUA_Executioner(&dat->skua_dat[l]);
			if (success != 0) {mError(simulation_fail); return -1;}
			dat->total_steps = dat->total_steps + dat->skua_dat[l].total_steps;
			
			//Store perturbed result in temp location
			dat->param_dat[i].qAvg.edit(l, 0, dat->skua_dat[l].finch_dat[i].uAvg);
			dat->param_dat[i].Qst.edit(l, 0, dat->skua_dat[l].param_dat[i].Qstnp1);
			dq_dc = ( qEPS - dat->param_dat[i].qAvg(l,0) ) / eps;
			dat->param_dat[i].dq_dc.edit(l, 0, dq_dc);
			
		}
		else
		{
			//Perturb the ith value and form the total pressure
			double tempPT = 0.0;
			for (int j=0; j<dat->magpie_dat.sys_dat.N; j++)
			{
				if (i == j)
					tempPT = tempPT + Pstd(dat->finch_dat[j].unp1(l,0)+eps, dat->magpie_dat.sys_dat.T);
				else
					tempPT = tempPT + Pstd(dat->finch_dat[j].unp1(l,0), dat->magpie_dat.sys_dat.T);
			}
			dat->magpie_dat.sys_dat.PT = tempPT;
			
			//Perturb the ith concentration
			for (int j=0; j<dat->magpie_dat.sys_dat.N; j++)
			{
				if (j == i)
					dat->magpie_dat.gpast_dat[j].y = Pstd(dat->finch_dat[j].unp1(l,0)+eps,dat->magpie_dat.sys_dat.T)/dat->magpie_dat.sys_dat.PT;
				else if (dat->param_dat[j].Adsorbable == true)
					dat->magpie_dat.gpast_dat[j].y = Pstd(dat->finch_dat[j].unp1(l,0),dat->magpie_dat.sys_dat.T)/dat->magpie_dat.sys_dat.PT;
				else
					dat->magpie_dat.gpast_dat[j].y = 0.0;
			}
			
			//Call MAGPIE to evaluate perturbed adsorption
			success = MAGPIE((void *)&dat->magpie_dat);
			if (success < 0 || success > 3) {mError(simulation_fail); return -1;}
			else success = 0;
			dat->total_steps = dat->total_steps + dat->magpie_dat.sys_dat.total_eval;
			
			//Store perturbed result in temp location
			qEPS = dat->magpie_dat.sys_dat.qT * dat->magpie_dat.gpast_dat[i].x;
			
			
			//Adjust interior pressure back to true values
			tempPT = 0.0;
			for (int j=0; j<dat->magpie_dat.sys_dat.N; j++)
				tempPT = tempPT + Pstd(dat->finch_dat[j].unp1(l,0), dat->magpie_dat.sys_dat.T);
			dat->magpie_dat.sys_dat.PT = tempPT;
			
			//Perform MAGPIE simulation
			for (int j=0; j<dat->magpie_dat.sys_dat.N; j++)
			{
				if (dat->param_dat[j].Adsorbable == true)
					dat->magpie_dat.gpast_dat[j].y = Pstd(dat->finch_dat[j].unp1(l,0),dat->magpie_dat.sys_dat.T)/dat->magpie_dat.sys_dat.PT;
				else
					dat->magpie_dat.gpast_dat[j].y = 0.0;
			}
			
			//Call MAGPIE to evaluate adsorption
			success = MAGPIE((void *)&dat->magpie_dat);
			if (success < 0 || success > 3) {mError(simulation_fail); return -1;}
			else success = 0;
			dat->total_steps = dat->total_steps + dat->magpie_dat.sys_dat.total_eval;
			
			//Store the solutions
			if (dat->param_dat[i].Adsorbable == true)
			{
				dat->param_dat[i].qAvg.edit(l, 0, dat->magpie_dat.sys_dat.qT * dat->magpie_dat.gpast_dat[i].x);
				dat->param_dat[i].Qst.edit(l,0,Qst(Pstd(dat->finch_dat[i].unp1(l,0),dat->magpie_dat.sys_dat.T), (void *)&dat->magpie_dat, i));
			}
			else
			{
				dat->param_dat[i].qAvg.edit(l, 0, 0.0);
				dat->param_dat[i].Qst.edit(l,0,0.0);
			}
			
			
			dq_dc = ( qEPS - dat->param_dat[i].qAvg(l,0) ) / eps;
			dat->param_dat[i].dq_dc.edit(l, 0, dq_dc);
			
		}
	}
	
	return dq_dc;
}