/* This file is part of Cloudy and is copyright (C)1978-2006 by Gary J. Ferland
 * For conditions of distribution and use see copyright notice in license.txt */
/* 
 * this is the series of routines that converge the pressure, temperature, 
 * electron density, and ionization, for a zone.  Ideally, only the top
 * routine, ConvPresTempEdenIoniz, should be public 
 */

/*ConvIterCheck check whether model has converged or whether more iterations
 * are needed - implements the iter to converg comnd */
void ConvIterCheck( void );

/* returns true if pressure is converged */
int lgConvPres(void);

/* returns true if heating-cooling is converged */
int lgConvTemp(void);

/* returns true if electron density is converged */
int lgConvEden(void);

/*ConvInitSolution drive search for initial solution at illuminated face,
 * called by cloudy, returns 1 if outside conditions appropriate for cloudy  */
int ConvInitSolution(void);

/*ConvPresTempEdenIoniz solve for current pressure, calls PressureChange, ConvTempEdenIoniz,
 * called by cloudy 
 * returns 0 if ok, 1 if disaster */
int ConvPresTempEdenIoniz(void);

/*PressureChange evaluate the current pressure, and change needed to get it to PresTotlInit,
 * return value is true is density was changed, false if no changes were necessary */
int PressureChange(
	/* this is change factor, 1 at first, becomes smaller as oscillations occur */
	double dP_chng_factor );

/*ConvTempEdenIoniz determine  temperature, called by ConPresTempEdenIoniz,
 * calls ConvEdenIoniz to get electron density and ionization 
 * returns 0 if ok, 1 if disaster */
int ConvTempEdenIoniz(void);

/*ConvEdenIoniz called by ConvTempEdenIoniz and ConvInitIonize, 
 * it calls ConvIoniz and converges the electron density 
 * returns 1 if error condtion, 0 if ok */
int ConvEdenIoniz(void);
 
/*ConvIoniz called by ConvEdenIonz, it calls ConvBase until converged 
 * returns 1 if error condtion, 0 if ok */
int ConvIoniz(void);

/*ConvFail handle conergece failure */
void ConvFail(
	/* chMode is one of "pres", "eden", "ioni", "pops", "grai", "temp" */
	const char chMode[], /* chMode[5] */
	const char chDetail[] );

/*ConvBase main routine to drive ionization solution for all species, find total opacity
 * called by ConvIoniz/
 *lgConverg check whether ionization of element nelem has converged */
int ConvBase(
	/* this tells how many times ConvBase has been called by ConvIoniz while
	 * trying to converge electron density == 0 on first call - allows
	 * logic in ConvBase to check for ots oscillations */
	 long loopi);

/*eden_sum sum free electron density over all species, sets variable erredn.EdenTrue
 * called by ConvEdenIoniz which actually controls the electron density updates 
 * returns 0 if all is ok, 1 if need to abort calc */
int eden_sum(void);

/*
 * the variables that deal with the convergence of the model 
 */

EXTERN struct t_conv {

	/* this says why the electron density did not converge,  */
	char *chConvEden;

	/* this says why the ionization did not converge, reasons can be a large
	 * change in the level of ionization, or in the heating */
	char *chConvIoniz;

	/* this gives the reason the model was declared not converged
	* when 'iter to convergence' command is given */
	char *chNotConverged;

	/* this flag is used in ConvPresTempEdenIoniz to check that ionization has converged */
	int lgConvIoniz;

	/* this flag is used in ConvPresTempEdenIoniz to check that populations have converged */
	int lgConvPops;

	/* when the lgConvIoniz flag is set false, the old and new numbers,
	 * the reason for the lack of convergence, should be set to following */
	double BadConvIoniz[2];

	/* this will count the number of ionizations in one call from ConPvPresTempIoniz*/
	long int nPres2Ioniz;
	/* a limit to the above, in case where one zone takes forever to not converge,
	 * usually very large, set with SET PRESIONIZ command */
	long int limPres2Ioniz;

	/* this will count the number of ionizations in all calls after zero
	 * can be used to determine very first pass through initial creation */
	long int nTotalIoniz;

	/* conv.lgSearch is true during initial temp-ion search phase,
	 * false after first zone established */
	int lgSearch;

	/* remember the average electron density error */
	float AverEdenError;

	/* remember the biggest and average heating-cooling error */
	float BigHeatCoolError;
	float AverHeatCoolError;

	/* remember the biggest and average pressure error */
	float BigPressError;
	float AverPressError;

	/* flag set in ConvBase, saying whether ionization stage is trimmed down */
	int lgIonStageTrimed;

	/* this says whether cooling-heating deriv is changing sign */
	int lgCmHOsc;

	/* this says whether temp is changing sign */
	int lgTOscl;

	/* this says whether electron density is changing sign */
	int lgEdenOscl;

	/* this is true if ots rates are oscillating, and this is why ionization
	 * is not converged */
	int lgOscilOTS ;

	/* true if temperature is converged, false if not */
	int lgConvTemp;

	/* true if pressure is converged, false if not */
	int lgConvPres;

	/* true when the electron density has converged */
	int lgConvEden;

	/* true if level 2 lines were contributors to the ots rates, set in dimacool */
	int lgLevel2_OTS_Imp;

	/* true if level 2 lines were contributors to the cooling, set in dimacool */
	int lgLevel2_Cool_Imp;

	/* total number of all falures, used to trigger abort */
	long int nTotalFailures;

	/*nTeFail number of temperature failures*/
	long int nTeFail;

	/*failmx is largest relative error in heating cooling match*/
	float failmx;

	/*nPreFail is number of pressure failures*/
	long int nPreFail;

	/*nNeFail is number of electron density failures*/
	long int nNeFail;

	/* remember the biggest electron density error as test of convergence quality */
	float BigEdenError;

	/*nIonFail is number of ionization failures*/
	long int nIonFail;

	/*nIonFail is number of level population failures */
	long int nPopFail;

	/* number of ConvIoniz ionization failures */
	long int nConvIonizFails;

	/* flag saying that an ionization failure did occur in this zone, used to
	 * help choose next dr (failure may have been converged away by time nextdr is called */
	int lgConvIonizThisZone;

	/* number of grain ionization balance failures */
	long int nGrainFail;

	/* LimFail is limit to number of te failures, set with "failures" cmnd */
	long int LimFail;

	/* lgMap is option to map failures */
	int lgMap;

	/* zones where converge problems occurred */
	long int ifailz[12];

	/* which electron density solver to use? 
	 * set with set eden solver command, simple and new */
	char chSolverEden[20];

	/* which temperature density solver to use? 
	 * set with set eden solver command, default and brent */
	char chSolverTemp[20];

	/* flag saying that calculation stopped for bad reason
	 * mostly set in lgEndfun */
	int lgBadStop;

	/* says "interate to convergence" command given */
	int lgAutoIt;

	/* a convergence criteria */
	float autocv;

	/* this is relative error in the electron density we want
	 * set in zero to 0.01
	 * reset with set eden error command */
	double EdenErrorAllowed;

	/* this is relative error in the pressure,
	 * initialized to 0.02 in 
	 * reset with set pressure error command */
	float PressureErrorAllowed;

	/* allowed error in heating - cooling balance, set with TOLERANCE command
	 * default set in zerologic */
	float HeatCoolRelErrorAllowed;

	/* these are used to retain the density pressure history in current zone,
	 * can be output with punch pressure history */
	double *hist_pres_density , *hist_pres_current , *hist_pres_correct;
	long int hist_pres_npres , hist_pres_nzone , hist_pres_limit;

	/* these are used to retain the temp/heat/cooling history in current zone,
	 * can be output with punch temperature history */
	double *hist_temp_temp , *hist_temp_heat , *hist_temp_cool;
	long int hist_temp_ntemp , hist_temp_nzone , hist_temp_limit;

} conv;
