/* This file is part of Cloudy and is copyright (C) 1978-2004 by Gary J. Ferland.
 * For conditions of distribution and use, see copyright notice in license.txt */
/*ParseSet scan parameters off SET command */
#include "cddefines.h"
#include "physconst.h"
#include "geometry.h"
#include "kshllenr.h"
#include "prt.h"
#include "co.h"
#include "hextra.h"
#include "rt.h"
#include "phycon.h"
#include "path.h"
#include "map.h"
#include "hmi.h"
#include "dense.h"
#include "converge.h"
#include "secondaries.h"
#include "rfield.h"
#include "ionbal.h"
#include "numderiv.h"
#include "dynamics.h"
#include "iso.h"
#include "punch.h"
#include "stopcalc.h"
#include "opacity.h"
#include "hydrogenic.h"
#include "peimbt.h"
#include "radius.h"
#include "atmdat.h"
#include "continuum.h"
#include "grains.h"
#include "grainvar.h"
#include "parse.h"

#define NINT(X) ((long)((X) < 0. ? (X)-0.5 : (X)+0.5))

void ParseSet(char *chCard )
{
	int lgEOL ;
	long int i, 
		ip;
	char *chEnd;
	char chString[INPUT_LINE_LENGTH];

#	ifdef DEBUG_FUN
	fputs( "<+>ParseSet()\n", debug_fp );
#	endif

	/* first check for any strings within quotes - this will get the string
	 * and blank it out, so that these are not confused with keywords.  if
	 * double quotes not present routine returns null */
	GetQuote( chString , chCard , FALSE );

	/* commands to set certain variables, "SET XXX=" */
	if( lgMatch("ASSE",chCard) &&lgMatch(" FPE",chCard) )
	{
		/* set crash assert command, to tell the code to throw an FPE when an
		 * assert is thrown - poor way to get a traceback on an assert */
		lgAssertFPE = TRUE;
	}
	else if( lgMatch(" CHA",chCard) )
	{
		/* set log of minimum charge transfer rate for high ions and H
		 * default of 1.92e-10 set in zero */
		i = 5;
		atmdat.HCTAlex = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			NoNumb(chCard);
		}
		if( atmdat.HCTAlex < 0. )
		{
			atmdat.HCTAlex = pow(10.,atmdat.HCTAlex);
		}
	}

	else if( lgMatch("CSUP",chCard) )
	{
		/* force secondary ionization rate, log entered */
		i = 5;
		secondaries.SetCsupra = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		secondaries.lgCSetOn = TRUE;
		if( lgEOL )
		{
			NoNumb(chCard);
		}
		secondaries.SetCsupra = (float)pow(10.f,secondaries.SetCsupra);
	}

	else if( lgMatch(" D/H",chCard) )
	{
		/* set deuterium abundance, D to H ratio */
		i = 5;
		hydro.D2H_ratio = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( hydro.D2H_ratio <= 0. || lgMatch( " LOG", chCard ) )
		{
			hydro.D2H_ratio = pow(10.f,hydro.D2H_ratio);
		}
		if( lgEOL )
		{
			NoNumb(chCard);
		}
	}

	else if( lgMatch("12C1",chCard) )
	{
		/* set the 12C to 13C abundance ratio - default is 30 */
		i = 5;
		/* first two numbers on the line are 12 and 13 - we don't want them */
		co.RatioC12O16_2_C13O16 = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		co.RatioC12O16_2_C13O16 = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);

		/* now we can get the ratio */
		co.RatioC12O16_2_C13O16 = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
			NoNumb(chCard);

		if( co.RatioC12O16_2_C13O16 <= 0. || lgMatch( " LOG", chCard ) )
			co.RatioC12O16_2_C13O16 = (float)pow(10.f,co.RatioC12O16_2_C13O16);
	}

	/* set dynamics ... */
	else if( lgMatch("DYNA",chCard) )
	{
		/* set dynamics advection length */
		if( lgMatch("ADVE",chCard) && lgMatch("LENG",chCard) )
		{
			/* <0 => relative fraction of length, + val in cm */
			i = 5;
			dynamics.AdvecLengthInit = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			/* if fraction is present, then number was linear fraction, if not present
			* then physical length in cm, log10 */
			if( lgMatch("FRAC",chCard) )
			{
				/* we scaned in the number, if it is a negative then it is the log of the fraction */
				if( dynamics.AdvecLengthInit <= 0. )
					dynamics.AdvecLengthInit = pow(10.,dynamics.AdvecLengthInit);

				/* make neg sign as flag for this in dynamics.c */
				dynamics.AdvecLengthInit *= -1.;
			}
			else
			{
				/* fraction did not occur, the number is the log of the length in cm -
				* convert to linear cm */
				dynamics.AdvecLengthInit = pow(10.,dynamics.AdvecLengthInit);
			}
		}
		else if( lgMatch("PRES",chCard) && lgMatch("MODE",chCard) )
		{
			dynamics.lgSetPresMode = TRUE;
			if( lgMatch("SUBS",chCard) )
			{
				/* subsonic */
				strcpy( dynamics.chPresMode , "subsonic" );
			}
			else if( lgMatch("SUPE",chCard) )
			{
				/* supersonic */
				strcpy( dynamics.chPresMode , "supersonic" );
			}
			else if( lgMatch("STRO",chCard) )
			{
				/* strong d */
				strcpy( dynamics.chPresMode , "strongd" );
			}
			else if( lgMatch("ORIG",chCard) )
			{
				/* original */
				strcpy( dynamics.chPresMode , "original" );
			}
		}
		else if( lgMatch("ANTI",chCard) && lgMatch("DEPT",chCard) )
		{
			dynamics.lgSetPresMode = TRUE;
			strcpy( dynamics.chPresMode , "antishock" );
			/* shock depth */
			i = 5;
			/* get log of shock depth in cm */
			dynamics.ShockDepth = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			dynamics.ShockDepth = pow( 10., dynamics.ShockDepth );
		}
		else if( lgMatch("ANTI",chCard) && lgMatch("MACH",chCard) )
		{
			dynamics.lgSetPresMode = TRUE;
			strcpy( dynamics.chPresMode , "antishock-by-mach" );
			/* Mach number */
			i = 5;
			/* get (isothermal) Mach number where we want antishock to occur */
			dynamics.ShockMach = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		}
		else if( lgMatch("SHOC",chCard) && lgMatch("DEPT",chCard) )
		{
			dynamics.lgSetPresMode = TRUE;
			strcpy( dynamics.chPresMode , "shock" );
			/* shock depth */
			i = 5;
			/* get log of shock depth in cm */
			dynamics.ShockDepth = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			dynamics.ShockDepth = pow( 10., dynamics.ShockDepth );
		}
	}

	else if( lgMatch("DIDZ",chCard) )
	{
		/* set parameter to do with choice of dr;
		 * par is the largest optical depth to allow in the zone. 
		 * >>chng 96 jan 08 had been two numbers - dtau1 removed */
		i = 5;
		radius.drChange = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( radius.drChange <= 0. )
		{
			radius.drChange = (float)pow(10.f,radius.drChange);
		}
		if( lgEOL )
		{
			NoNumb(chCard);
		}

	}

	/* something to do with electron density */
	else if( lgMatch("EDEN",chCard) )
	{
		/* >>chng 02 apr 20, from ERROR to TOLERANCE to be parallel to set temp equivalent,
		 * >>chng 04 jun 03, but also accept error as keyword */
		if( lgMatch("CONV",chCard) || lgMatch("ERRO",chCard))
		{
			/* keyword is eden convergence  
			 * set tolerance in eden match */
			i = 5;
			conv.EdenErrorAllowed = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			if( lgEOL )
			{
				NoNumb(chCard);
			}

			if( conv.EdenErrorAllowed < 0. )
			{
				conv.EdenErrorAllowed = pow(10.,conv.EdenErrorAllowed);
			}
		}

		else if( lgMatch("SOLV",chCard) )
		{
			/* the electron density solver */
			if( lgMatch("NEW",chCard) ) 
			{
				/* new method */
				strcpy( conv.chSolverEden , "new" );
			}
			else
			{
				/* default method */
				strcpy( conv.chSolverEden , "simple" );
			}
		}
		else 
		{
			/* no keyword, assume log of electron density */
			i = 5;
			/* set the electron density */
			dense.EdenSet = (float)pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));
			/* warn that this model is meaningless */
			phycon.lgPhysOK = FALSE;
		}
	}

	/* these are the "set leiden hack" commands, used to turn of physics and
	 * sometimes replace with simple approximation */
	else if( lgMatch("LEID",chCard ) && lgMatch("HACK",chCard ) )
	{
		if( lgMatch("PAH ",chCard ) && lgMatch("BAKE",chCard ) )
		{
			/* turn on simnple PAH heating from Bakes & Tielens - this is very approximate */
			/*>>>refer	PAH	heating	Bakes, E.L.O., & Tielens, A.G.G.M. 1994, ApJ, 427, 822 */
			gv.lgLeidenBakesPAH_heat = TRUE;
			/* warn that this model is not the best we can do */
			phycon.lgPhysOK = FALSE;
		}

		else if( lgMatch( "GRAI" , chCard ) && lgMatch( "OPAC" , chCard ) )
		{
			/* the grains leiden hack option - clobber the uv grain opacities */
			gv.lgLeidenGrnOpacHack = TRUE;
			/* warn that this model is not the best we can do */
			phycon.lgPhysOK = FALSE;
		}

		else if( lgMatch( "H2* " , chCard ) && lgMatch( " OFF" , chCard ) )
		{
			/* turn off reactions with H2* in the chemistry network */
			hmi.lgLeiden_Keep_ipMH2s = FALSE;
			/* warn that this model is not the best we can do */
			phycon.lgPhysOK = FALSE;
		}

		else if( lgMatch( "COSM" , chCard ) && lgMatch( "HEAV" , chCard )
			     && lgMatch( "OFF" , chCard ))
		{
			/* the grains leiden hack option - clobber the uv grain opacities */
			hextra.lgLeidenCsupraHack = TRUE;
			/* warn that this model is not the best we can do */
			phycon.lgPhysOK = FALSE;
		}
        else if( lgMatch( "CR " , chCard ) && lgMatch( " OFF" , chCard ) )
		{
			/* the CR leiden hack option - turn off CR excitations */
			hmi.lgLeidenCRHack = FALSE;
			
		}
		else if( lgMatch("RATE",chCard ) &&  lgMatch("UMIS",chCard ))
		{
				/* This command will use the rates given in the UMIST database,  It
				will set to zero many reactions in hmole_step.c that are not
				in UMIST */
			
			co.lgUMISTrates = FALSE;

		}
	}
		

	/* set H2 ... */
	else if( lgMatch(" H2 ",chCard) )
	{
		i = 5;
		ip = (long int)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( ip != 2 )
		{
			fprintf( ioQQQ, " The first number on this line must be the 2 in H2\n Sorry.\n" );
			puts( "[Stop in ParsePunch]" );
			cdEXIT(EXIT_FAILURE);
		}

		/* whioch approximation to Solomon process */
		if( lgMatch("SOLO",chCard) )
		{
			if( lgMatch("TH85", chCard ) )
			{
				/* rate from is eqn A8 of Tielens and Hollenbach 85a, */
				hmi.chSolomon = 'T';
			}
			else if( lgMatch( "BD96" , chCard ) )
			{
				/* this rate is equation 23 of
				 *>>refer	H2	dissoc	Bertoldi, F., & Draine, B.T., 1996, 458, 222 */
				/* this is the default */
				hmi.chSolomon = 'B';
			}
			else
			{
				fprintf( ioQQQ, " The first number on this line must be the 2 in H2\n Sorry.\n" );
				puts( "[Stop in ParsePunch]" );
				cdEXIT(EXIT_FAILURE);
			}
		}

		/* series of commands that deal with grains */
		/* which approximation to grain formation pumping */
		if( lgMatch("GRAI",chCard ) && lgMatch("FORM",chCard ) && lgMatch("PUMP",chCard )  )
		{
			if( lgMatch( "DB96" , chCard ) )
			{
				/* Draine & Bertoldi 1996 */
				hmi.chGrainFormPump = 'D';
			}
			else if( lgMatch( "TAKA" , chCard ) )
			{
				/* Takahashi 2001 */
				hmi.chGrainFormPump = 'T';
			}
			else if( lgMatch( "THER" , chCard ) )
			{
				/* thermal distribution, upper right column of page 239 of
				*>>refer	H2	formation	Le Bourlot, J, 1991, A&A, 242, 235 */
				hmi.chGrainFormPump = 't';
			}
			else
			{
				fprintf( ioQQQ, " The grain form pump option is wrong.\n Sorry.\n" );
				puts( "[Stop in ParsePunch]" );
				cdEXIT(EXIT_FAILURE);
			}
		}

		/* which approximation to Jura rate */
		else if( lgMatch("JURA",chCard) )
		{
			if( lgMatch("TH85", chCard ) )
			{
				/* rate from is eqn A8 of Tielens and Hollenbach 85a*/
				hmi.chJura = 'T';
			}
			else if( lgMatch( "CT02" , chCard ) )
			{
				/* this rate is equation cazeux & Tielens */
				hmi.chJura = 'C';
			}
			else if( lgMatch( "SN99" , chCard ) )
			{
				/* this rate is from Sternberg & Neufeld 99 */
				hmi.chJura = 'S';
			}
			else
			{
				fprintf( ioQQQ, " The Jura rate option is wrong.\n Sorry.\n" );
				puts( "[Stop in ParsePunch]" );
				cdEXIT(EXIT_FAILURE);
			}
		}

		/* what temperature to use for binding energy, Tad in Le Bourlot, J., 2000, A&A, 360, 656-662  */
		else if( lgMatch(" TAD ",chCard) )
		{
			hmi.Tad = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			if( lgEOL )
				NoNumb(chCard);
			/* log if <= 10. unless linear key appears too */
			if( hmi.Tad <=10. && !lgMatch("LINE",chCard) )
				hmi.Tad = (float)pow(10.,hmi.Tad);
		}

		else if( lgMatch("FRAC",chCard ) )
		{
			/* this is special option to force H2 abundance to value for testing
			 * this factor will multiply the hydrogen density to become the H2 density
			 * no attempt to conserve particles, or do the rest of the molecular equilibrium
			 * set consistently is made */
			hmi.frac_abund = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			if( lgEOL )
				NoNumb( chCard );

			/* a number <= 0 is the log of the ratio */
			if( hmi.frac_abund <= 0. )
				hmi.frac_abund = pow(10., hmi.frac_abund);
			/* don't let it exceed 0.5 */
			/* >>chng 03 jul 19, from 0.5 to 0.4999, do not want atomic density exactly zero */
			hmi.frac_abund = MIN2(0.49999 , hmi.frac_abund );
		}
	}

	else if( lgMatch("PRES",chCard) && lgMatch("IONI",chCard) )
	{
		/* set limit to number of calls from pressure to ionize solver,
		 * this set limit to conv.nPres2Ioniz */
		i = 5;
		conv.limPres2Ioniz = (long)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			NoNumb(chCard);
		}
		else if( conv.limPres2Ioniz <= 0 )
		{
			fprintf( ioQQQ, " The limit must be greater than zero.\n Sorry." );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}
	/* something to do with pressure */
	else if( lgMatch("PRES",chCard) )
	{
		/* tolerance on pressure convergence */
		if( lgMatch("CONV",chCard) )
		{
			/* keyword is tolerance 
			 * set tolerance in pressure match */
			i = 5;
			conv.PressureErrorAllowed = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			if( lgEOL )
			{
				NoNumb(chCard);
			}

			if( conv.PressureErrorAllowed < 0. )
			{
				conv.PressureErrorAllowed = (float)pow(10.f,conv.PressureErrorAllowed);
			}
		}
		else
		{
			/* >>chng 04 mar 02, printout had been wrong, saying TOLErange
			 * rather than CONVergence.  Nick Abel */
			fprintf( ioQQQ, " I didn\'t recognize a key on this SET PRESSURE line.\n" );
			fprintf( ioQQQ, " The ones I know about are: CONVergence.\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	else if( lgMatch("SPEC",chCard) )
	{
		/* "set spectrum" for optional parameters for punch spectrum command */
		if( lgMatch("RANG" , chCard ) )
		{
			/* wavelength range option */
			i = 5;
			punch.cp_range_min[punch.cp_npun] = (float)pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));
			punch.cp_range_max[punch.cp_npun] = (float)pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));
			if( lgEOL )
			{
				NoNumb(chCard);
			}
			/* contfirm that wavelengths are in correct order */
			if( punch.cp_range_min[punch.cp_npun] >= punch.cp_range_max[punch.cp_npun] )
			{
				fprintf( ioQQQ, " The limits must be in increasing order.\n" );
				puts( "[Stop in ParseSet]" );
				cdEXIT(EXIT_FAILURE);
			}
		}
		else if( lgMatch("RESO" , chCard ) )
		{
			/* resolving power, default is zero, which means leave at native resolution */
			i = 5;
			punch.cp_resolving_power[punch.cp_npun] = (float)pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));
			if( lgEOL )
			{
				NoNumb(chCard);
			}
		}
	}

	else if( lgMatch(" DR ",chCard) )
	{
		/* set zone thickness by forcing drmax and drmin */
		i = 5;
		/* at this stage linear, but default is log */
		radius.sdrmax = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( !lgMatch("LINE",chCard) ) 
		{
			/* linear was not on command, so default is log */
			radius.sdrmax = pow( 10. , radius.sdrmax );
		}
		if( lgEOL )
		{
			NoNumb(chCard);
		}
		/* NB these being equal are tested in convinittemp to set dr */
		radius.sdrmin = radius.sdrmax;
		if( radius.sdrmax < DEPTH_OFFSET*1e4 )
		{
			fprintf( ioQQQ, "\n Thicknesses less than about %.0e will NOT give accurate results. If tricking the code\n",
					 DEPTH_OFFSET*1e4 );
			fprintf( ioQQQ, " into computing emissivities instead of intensities, try to instead use a thickness of unity,\n" );
			fprintf( ioQQQ, " and then multipy (divide) the results by the necessary thickness (product of densities).\n\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	else if( lgMatch("DRMA",chCard) )
	{
		/* set maximum zone thickness */
		i = 5;
		radius.sdrmax = pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));
		if( lgEOL )
		{
			NoNumb(chCard);
		}
	}

	else if( lgMatch("DRMI",chCard) )
	{
		/* set minimum zone thickness */
		i = 5;
		radius.sdrmin = pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));
		if( lgEOL )
		{
			NoNumb(chCard);
		}
		radius.lgSMinON = TRUE;
	}

	else if( lgMatch("FLXF",chCard) )
	{
		/* faintest continuum flux to consider */
		i = 5;
		rfield.FluxFaint = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			NoNumb(chCard);
		}
		if( rfield.FluxFaint < 0. )
		{
			rfield.FluxFaint = (float)pow(10.f,rfield.FluxFaint);
		}
	}

	/* >>chng 00 dec 08, command added by Peter van Hoof */
	else if( lgMatch("NFNU",chCard) )
	{
		/* set nFnu [incident_reflected] [incident_transmitted] [diffuse_inward] [diffuse_outward]
		 *   command for specifying what to include in the nFnu entries in LineSv */
		/* >>chng 01 nov 12, also accept form with space */
		/* "incident reflected" keyword */
		prt.lgSourceReflected = lgMatch("NT R",chCard) || lgMatch("NT_R",chCard);
		/* "incident transmitted" keyword */
		prt.lgSourceTransmitted = lgMatch("NT_T",chCard) || lgMatch("NT T",chCard);
		/* "diffuse inward" keyword */
		prt.lgDiffuseInward = lgMatch("SE_I",chCard) || lgMatch("SE I",chCard);
		/* "diffuse outward" keyword */
		prt.lgDiffuseOutward = lgMatch("SE_O",chCard) || lgMatch("SE O",chCard);

		/* at least one of these needs to be set ! */
		if( ! ( prt.lgSourceReflected || prt.lgSourceTransmitted ||
			prt.lgDiffuseInward || prt.lgDiffuseOutward ) )
		{
			fprintf( ioQQQ, " set nFnu expects one or more of the following keywords:\n" );
			fprintf( ioQQQ, " INCIDENT_REFLECTED, INCIDENT_TRANSMITTED, DIFFUSE_INWARD, DIFFUSE_OUTWARD\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
		/* automatically print the nFnu items in the output - it is not necessary to also include
		 * a print continua command if this is entered */
		prt.lgPrnDiff = TRUE;
	}

	else if( lgMatch("IND2",chCard) )
	{
		if( lgMatch(" ON ",chCard) )
		{
			/* set flag saying to off or on induced two photon processes */
			iso.lgInd2nu_On = TRUE;
		}
		else if( lgMatch(" OFF",chCard) )
		{
			iso.lgInd2nu_On = FALSE;
		}
		else
		{
			fprintf( ioQQQ, " set ind2 needs either ON or OFF.\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	else if( lgMatch("TEMP",chCard) )
	{
		/* set something to do with temperature, currently solver, tolerance, floor */
		if( lgMatch("SOLV",chCard) )
		{
			/* solver */
			/* the electron density solver */
			if( lgMatch("NEW",chCard) ) 
			{
				/* new method */
				strcpy( conv.chSolverTemp , "new" );
			}
			else
			{
				/* default method */
				strcpy( conv.chSolverTemp , "simple" );
			}
		}
		else if( lgMatch("FLOO",chCard) )
		{
			i = 5;
			StopCalc.TeFloor = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);

			/*  linear option */
			if( StopCalc.TeFloor <= 10. && !lgMatch("LINE",chCard) )
			{
				StopCalc.TeFloor = (float)pow(10.f,StopCalc.TeFloor);
			}

			if( lgEOL )
			{
				NoNumb(chCard);
			}

			if( StopCalc.TeFloor < 2.8 )
			{
				fprintf( ioQQQ, " TE < 3K, reset to 2.8K.\n" );
				StopCalc.TeFloor = 2.8f;
			}
		}
		else if( lgMatch("CONV",chCard) || lgMatch("TOLE",chCard) )
		{
			/* error tolerance in heating cooling match, number is error/total */
			i = 5;
			conv.HeatCoolRelErrorAllowed = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			if( lgEOL )
			{
				NoNumb(chCard);
			}
			if( conv.HeatCoolRelErrorAllowed <= 0. )
			{
				conv.HeatCoolRelErrorAllowed = (float)pow(10.f,conv.HeatCoolRelErrorAllowed);
			}
		}
		else
		{
			fprintf( ioQQQ, "\nI did not recognize a keyword on this SET TEPERATURE command.\n%s\n" , chCard);
			fprintf( ioQQQ, "The keywords are SOLVer, FLOOr, and CONVergence.\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	else if( lgMatch("TEST",chCard) )
	{
		/* set flag saying to turn on some test - this is in cddefines.h in the global namespace */
		lgTestCodeEnabled = TRUE;
	}

	else if( lgMatch("TRIM",chCard) )
	{
		/* set trim upper or lower, for ionization stage trimming
		 * in routine TrimStage */
		i = 5;
		if( lgMatch("UPPE",chCard) )
		{
			/* set trim upper */
			double save = ionbal.trimhi;
			ionbal.trimhi = pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));
			if( lgEOL  && lgMatch(" OFF",chCard) )
			{
				/* turn off upward trimming */
				lgEOL = FALSE;
				ionbal.lgTrimhiOn = FALSE;
				/* reset high limit to proper value */
				ionbal.trimhi = save;
			}
		}

		else if( lgMatch("LOWE",chCard) )
		{
			/* set trim lower */
			ionbal.trimlo = pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));
		}

		/* turn off ionization stage trimming */
		else if( lgMatch("SMAL",chCard) || lgMatch(" OFF",chCard) )
		{
			/* set small limits to both upper and lower limit*/
			ionbal.trimlo = SMALLFLOAT;
			ionbal.trimhi = SMALLFLOAT;
			lgEOL = FALSE;
		}

		else
		{
			/* set trim upper */
			ionbal.trimhi = pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));

			/* set trim lower to same number */
			ionbal.trimlo = ionbal.trimhi;
		}

		if( lgEOL )
		{
			NoNumb(chCard);
		}

		if( ionbal.trimlo >= 1. || ionbal.trimhi >= 1. )
		{
			fprintf( ioQQQ, " number must be negative since log\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	else if( lgMatch("SKIP",chCard) )
	{
		/* skip punch command, for punching every n't point */
		i = 5;
		punch.ncPunchSkip = (long)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			NoNumb(chCard);
		}
	}

	else if( lgMatch("EAKH",chCard) )
	{
		/* set WeakHeatCool, threshold on punch heating and cooling, default 0.05 */
		i = 5;
		punch.WeakHeatCool = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);

		if( lgEOL )
		{
			NoNumb(chCard);
		}

		if( punch.WeakHeatCool < 0. )
		{
			punch.WeakHeatCool = (float)pow(10.f,punch.WeakHeatCool);
		}
	}

	else if( lgMatch("KSHE",chCard) )
	{
		/* upper limit to opacities for k-shell ionizaiton */
		i = 5;
		KshllEnr.EnergyKshell = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			NoNumb(chCard);
		}

		if( KshllEnr.EnergyKshell == 0. )
		{
			/* arg of 0 causes upper limit to energy array */
			KshllEnr.EnergyKshell = rfield.egamry;
		}

		else if( KshllEnr.EnergyKshell < 194. )
		{
			fprintf( ioQQQ, " k-shell energy must be greater than 194 Ryd\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	else if( lgMatch("NCHR",chCard) )
	{
		/* option to set the number of charge states for grain model */
		long ii = 5;
		double val = FFmtRead(chCard,&ii,INPUT_LINE_LENGTH,&lgEOL);

		if( lgEOL )
		{
			NoNumb(chCard);
		}
		else
		{
			long nChrg = NINT(val);
			if( nChrg < 2 || nChrg > NCHS )
			{
				fprintf( ioQQQ, " illegal value for number of charge states: %ld\n", nChrg );
				fprintf( ioQQQ, " choose a value between 2 and %ld\n", NCHS );
				fprintf( ioQQQ, " or increase NCHS in grainvar.h and recompile\n" );
				puts( "[Stop in ParseSet]" );
				cdEXIT(EXIT_FAILURE);
			}
			else
			{
				SetNChrgStates(nChrg);
			}
		}
	}

	else if( lgMatch("NEGO",chCard) )
	{
		/* punch negative opacities if they occur, set negopac */
		opac.lgNegOpacIO = TRUE;
	}

	else if( lgMatch("NEND",chCard) )
	{
		/* default limit to number of zones to be computed
		 * only do this if nend is NOT currently left at its default
		 * nend is set to nEndDflt in routine zero
		 * this command only has effect if stop zone not entered */
		if( geometry.lgEndDflt )
		{
			i = 5;
			/* this is default limit to number of zones, change it to this value */
			geometry.nEndDflt = (long)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			geometry.lgEndDflt = FALSE;
			if( lgEOL )
			{
				NoNumb(chCard);
			}

			/* now change all limits, for all iterations, to this value */
			for( i=0; i < ITRDIM; i++ )
			{
				geometry.nend[i] = geometry.nEndDflt;
			}
		}
	}

	else if( lgMatch("TSQD",chCard) )
	{
		/* upper limit for highest density considered in the 
		 * Peimbert-style t^2 section of the printout */
		i = 5;
		peimbt.tsqden = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);

		if( lgEOL )
		{
			NoNumb(chCard);
		}
		peimbt.tsqden = (float)pow(10.f,peimbt.tsqden);
	}

	else if( lgMatch("NMAP",chCard) )
	{
		/* how many steps in plot or punch of heating-cooling map */
		i = 5;
		map.nMapStep = (long)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			NoNumb(chCard);
		}
	}

	else if( lgMatch("NUME",chCard) && lgMatch("DERI",chCard) )
	{
		/* this is an option to use numerical derivatives for heating and cooling */
		NumDeriv.lgNumDeriv = TRUE;
	}

	/* set path for certain files */
	else if( lgMatch("PATH",chCard) )
	{
		/* set path for picking up files
		 * format is for path to be within double quotes */
		lgDataPathSet = TRUE;

		/* 
		 * get any string within double quotes, and return it as
		 * null terminated string to chDataPath
		 * also sets name in OrgCard and chCard to blanks so that
		 * do not trigger off it later 
		GetQuote( chDataPath , chCard , TRUE );*/
		strcpy( chDataPath , chString );
		/* make sure path ends with proper end of line for unix, dos, or vms *
		 * do nothing in any of these cases */

		/* find end of string, null char */
		chEnd = strchr( chDataPath ,'\0' );
		if( chEnd==NULL )
		{
			fprintf( ioQQQ, " strchr returned NULL in ParseSet\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}

		/* set pointer to last valid char */
		--chEnd;

		/* now see what this char is */
		if( (*chEnd != '/' && *chEnd != ']') && *chEnd != '\\' )
		{
			/* string is not ended properly, we will have to do it */

			/* find which character to end with since none set above *
			 * there must have been something within the string to indicate 
			 * what type of system we are on */
			if(strchr( chDataPath , '/' ) != NULL )
			{
				/* there was a forward slash somewhere, so cat another to the end */
				strcat( chDataPath , "/" );
			}
			else if( strchr( chDataPath , '\\' ) != NULL )
			{
				/* there was a backward slash somewhere, so cat another to the end */
				strcat( chDataPath , "\\" );
			}
			else if( strchr( chDataPath , '[' ) != NULL )
			{
				/* there was a [ somewhere, so cat ] to the end */
				/* this should work on a vms machine, but I have not tried it */
				strcat( chDataPath , "]" );
			}
			/* if none of the above were triggered then we did not find anything, 
			 * just leave it alone, either user knew something we didn't, or will
			 * fail when we try to open the file later on */
		}
		/*printf(" final path is %s \n",chDataPath );*/
	}

	else if( lgMatch("PHFI",chCard) )
	{
		/* which version of PHFIT to use, 1995 or 1996 */
		i = 5;
		ip = (long)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			NoNumb(chCard);
		}

		if( ip == 1995 )
		{
			/* option to go back to old results, pre op */
			PhFitOn.lgPhFit = TRUE;
		}
		else if( ip == 1996 )
		{
			/* default is to use newer results, including opacity project */
			PhFitOn.lgPhFit = FALSE;
		}
		else
		{
			fprintf( ioQQQ, " Two possible values are 1995 and 1996.\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	/* set punch xxx command */
	else if( lgMatch("PUNC",chCard) )
	{
		if( lgMatch("HASH",chCard) )
		{
			char chTmpHashString[INPUT_LINE_LENGTH];
			/* specify the terminator between punch output sets - normally a set of hash marks */
			/* 
			* get any string within double quotes, and return it as
			* null terminated string to chDataPath
			* also sets name in OrgCard and chCard to blanks so that
			* do not trigger off it later 
			GetQuote( chTmpHashString , chCard , TRUE );*/
			strcpy( chTmpHashString , chString );
			if( strcmp( chTmpHashString , "return" ) == 0 )
			{
				sprintf(punch.chHashString , "\n" );
			}
			else
			{
				sprintf(punch.chHashString , chTmpHashString );
			}
		}

		else if( lgMatch("WIDT",chCard) )
		{
			/* set punch line width for contrast in continuum plots */
			i = 5;
			/* units are km/sec */
			punch.PunchLWidth = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);

			/* lgEOL if no number hit, could have been c */
			if( lgEOL )
			{
				if( lgMatch("  C " , chCard ) )
				{
					/* no number but __c_, so enter speed of light in km/s */
					punch.PunchLWidth = (float)(SPEEDLIGHT/1e5);
				}
				else
				{
					NoNumb(chCard);
				}
			}

			if( punch.PunchLWidth <= 0. )
			{
				fprintf( ioQQQ, " line width must be greater than zero.\n" );
				puts( "[Stop in ParseSet]" );
				cdEXIT(EXIT_FAILURE);
			}
			else if( punch.PunchLWidth > SPEEDLIGHT )
			{
				fprintf( ioQQQ, " line width must be entered in cm/s and less than c.\n" );
				puts( "[Stop in ParseSet]" );
				cdEXIT(EXIT_FAILURE);
			}
			/* this is the factor that is used to add the lines to the continuum,
			* 15 converts to cm/s */
			punch.PunchLWidth = (float)(SPEEDLIGHT/(punch.PunchLWidth*1e5));

		}
		else if( lgMatch("PREF",chCard) )
		{
			/* specify a prefix before all punch filenames */
			/* 
			* get any string within double quotes, and return it as
			* null terminated string to chDataPath
			* also sets name in OrgCard and chCard to blanks so that
			* do not trigger off it later 
			
			GetQuote( punch.chFilenamePrefix , chCard , TRUE );*/
			strcpy( punch.chFilenamePrefix , chString );
		}
		else if( lgMatch("FLUS",chCard) )
		{
			/* flus the output after every iteration */
			punch.lgFLUSH = TRUE;
		}
		else
		{
			fprintf( ioQQQ, " There should have been an option on this command.\n" );
			fprintf( ioQQQ, " Valid options are HASH and PREFIX.\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	/* set continuum .... options */
	else if( lgMatch("CONT" , chCard ) )
	{
		if( lgMatch("RESO" , chCard ) )
		{
			/* set resolution, get factor that will multiply continuum resolution that
			* is contained in the file continuum_mesh.dat */
			i = 5;
			continuum.ResolutionScaleFactor = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			if( lgEOL )
			{
				NoNumb(chCard);
			}
			/* negative numbers were logs */
			if( continuum.ResolutionScaleFactor <= 0. )
				continuum.ResolutionScaleFactor = pow(10.,continuum.ResolutionScaleFactor);
		}
		else if( lgMatch("SHIE" , chCard ) )
		{
			/* set continuum shielding function */
			/* these are all possible values of rt.nLineContShield,
			 * first is default, these are set with set continuum shielding */
			/*#define LINE_CONT_SHIELD_PESC	1*/
			/*#define LINE_CONT_SHIELD_FEDERMAN	2*/
			/*#define LINE_CONT_SHIELD_FERLAND	3*/
			if( lgMatch("PESC" , chCard ) )
			{
				rt.nLineContShield = LINE_CONT_SHIELD_PESC;
			}
			else if( lgMatch("FEDE" , chCard ) )
			{
				/* set continuum shielding federmain */
				rt.nLineContShield = LINE_CONT_SHIELD_FEDERMAN;
			}
			else if( lgMatch("FERL" , chCard ) )
			{
				rt.nLineContShield = LINE_CONT_SHIELD_FERLAND;
			}
			else if( lgMatch("NONE" , chCard ) )
			{
				/* turn off continuum shielding */
				rt.nLineContShield = 0;
			}
			else
			{
				fprintf( ioQQQ, " I didn\'t recognize a key on this SET CONTINUUM SHIELDing line.\n" );
				fprintf( ioQQQ, " The ones I know about are: PESC, FEDErman, & FERLand.\n" );
				puts( "[Stop in ParseSet]" );
				cdEXIT(EXIT_FAILURE);
			}
		}
		else
		{
			fprintf( ioQQQ, " I didn\'t recognize a key on this SET CONTINUUM line.\n" );
			fprintf( ioQQQ, " The ones I know about are: RESOlution.\n" );
			puts( "[Stop in ParseSet]" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	else
	{
		fprintf( ioQQQ, " I didnt recognize a key on this SET line.\n" );
		puts( "[Stop in ParseSet]" );
		cdEXIT(EXIT_FAILURE);
	}


#	ifdef DEBUG_FUN
	fputs( " <->ParseSet()\n", debug_fp );
#	endif
	return;
}

