예제 #1
0
boolean set_solver_types(void) {
  boolean nerr = 0;
  if( (g_solver_var_type = FindType(AddSymbol(SOLVER_VAR_STR))) == NULL ) {
    ERROR_REPORTER_HERE(ASC_PROG_FATAL,"'solver_var' not defined");
    nerr++;
  }
  if( (g_solver_int_type = FindType(AddSymbol(SOLVER_INT_STR))) == NULL ) {
    ERROR_REPORTER_HERE(ASC_PROG_ERROR,"'solver_int' not defined: MPS will not work");
    nerr++;
  }
  g_solver_binary_type = FindType(AddSymbol(SOLVER_BINARY_STR));
  if( g_solver_binary_type == NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_FATAL,"'solver_binary' not defined: MPS will not work");
    nerr++;
  }
  if( (g_solver_semi_type = FindType(AddSymbol(SOLVER_SEMI_STR))) == NULL ) {
    ERROR_REPORTER_HERE(ASC_PROG_FATAL,"'solver_semi' not defined: MPS will not work");
    nerr++;
  }

  LOWER_V = AddSymbol("lower_bound");
  UPPER_V = AddSymbol("upper_bound");
  RELAXED_V = AddSymbol("relaxed");
  NOMINAL_V = AddSymbol("nominal");
  FIXED_V = AddSymbol("fixed");
  INTERFACE_V = AddSymbol("interface");
  ODEATOL_V = AddSymbol("ode_atol");
  return nerr;
}
예제 #2
0
void var_set_incidencesF(struct var_variable *var,int32 n,
                         struct rel_relation **i)
{
  if(var!=NULL && n >=0) {
    if (n && i==NULL) {
      ERROR_REPORTER_HERE(ASC_PROG_ERR,"NULL i");
    }
    var->n_incidences = n;
    var->incidence = i;
    return;
  }
  ERROR_REPORTER_HERE(ASC_PROG_ERR,"NULL var, or n < 0");
}
예제 #3
0
double var_odeatol(struct var_variable *var){
	struct Instance *c;
	if(var==NULL||var->ratom==NULL){
		ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
		return -1;
	}
	c = ChildByChar(IPTR(var->ratom),ODEATOL_V);
	if(c==NULL){
		ERROR_REPORTER_HERE(ASC_PROG_ERR,"no '%s' field",SCP(ODEATOL_V));
		return -1;
	}
	return RealAtomValue(c);
}
예제 #4
0
/**
	This is the function called from "IMPORT fprops"

	It sets up the functions contained in this external library
*/
extern
ASC_EXPORT int fprops_register(){
	int result = 0;

	ERROR_REPORTER_HERE(ASC_USER_WARNING,"FPROPS is still EXPERIMENTAL. Use with caution.");

#define CALCFN(NAME,INPUTS,OUTPUTS) \
	result += CreateUserFunctionBlackBox(#NAME \
		, asc_fprops_prepare \
		, NAME##_calc /* value */ \
		, (ExtBBoxFunc*)NULL /* derivatives not provided yet*/ \
		, (ExtBBoxFunc*)NULL /* hessian not provided yet */ \
		, (ExtBBoxFinalFunc*)NULL /* finalisation not implemented */ \
		, INPUTS,OUTPUTS /* inputs, outputs */ \
		, NAME##_help /* help text */ \
		, 0.0 \
	) /* returns 0 on success */

#define CALCFNDERIV(NAME,INPUTS,OUTPUTS) \
	result += CreateUserFunctionBlackBox(#NAME \
		, asc_fprops_prepare \
		, NAME##_calc /* value */ \
		, NAME##_calc /* derivatives */ \
		, (ExtBBoxFunc*)NULL /* hessian not provided yet */ \
		, (ExtBBoxFinalFunc*)NULL /* finalisation not implemented */ \
		, INPUTS,OUTPUTS /* inputs, outputs */ \
		, NAME##_help /* help text */ \
		, 0.0 \
	) /* returns 0 on success */

	CALCFNDERIV(fprops_p,2,1);
	CALCFN(fprops_u,2,1);
	CALCFN(fprops_s,2,1);
	CALCFN(fprops_h,2,1);
	CALCFN(fprops_a,2,1);
	CALCFN(fprops_g,2,1);
	CALCFN(fprops_cp,2,1);
	CALCFN(fprops_cv,2,1);
	CALCFN(fprops_w,2,1);
	CALCFN(fprops_mu,2,1);
	CALCFN(fprops_lam,2,1);
	CALCFN(fprops_phsx_vT,2,4);
	CALCFN(fprops_Tvsx_ph,2,4);

#undef CALCFN

	if(result){
		ERROR_REPORTER_HERE(ASC_PROG_NOTE,"CreateUserFunction result = %d\n",result);
	}
	return result;
}
예제 #5
0
void var_set_nominal(struct var_variable *var, real64 nominal)
{
  struct Instance *c;
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return;
  }
  c = ChildByChar(IPTR(var->ratom),NOMINAL_V);
  if( c == NULL ) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"no 'nominal' field in var");
    /* WriteInstance(stderr,IPTR(var->ratom)); */
    return;
  }
  SetRealAtomValue(c,nominal,(unsigned)0);
}
예제 #6
0
real64 var_upper_bound(struct var_variable *var)
{
  struct Instance *c;
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return 0.0;
  }
  c = ChildByChar(IPTR(var->ratom),UPPER_V);
  if( c == NULL ) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"no 'upper_bound' field");
    /* WriteInstance(stderr,IPTR(var->ratom)); */
    return 0.0;
  }
  return( RealAtomValue(c) );
}
예제 #7
0
void var_set_upper_bound(struct var_variable *var, real64 upper_bound)
{
  struct Instance *c;
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return;
  }
  c = ChildByChar(IPTR(var->ratom),UPPER_V);
  if( c == NULL ) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"no 'upper_bound' field");
    /* WriteInstance(stderr,IPTR(var->ratom)); */
    return;
  }
  SetRealAtomValue(c,upper_bound,(unsigned)0);
}
예제 #8
0
/**
	@return 0 on success
*/
int datareader_tmy3_header(DataReader *d){
	Tmy3Location loc;
	d->data = ASC_NEW(Tmy3Data);
	DATA(d)->p = parseCreateFile(d->f);
	parse *p = DATA(d)->p;
	char rubbish[2049];

	if(!(
		parseLocation(p,&loc)
		&& parseStrExcept(p,"\r\n",rubbish,2048)
		&& parseEOL(p)
	)){
		ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Parser error in header part of file");
	}

	CONSOLE_DEBUG("TMY3 file for '%s' at (%.2fN,%.2fE)",loc.stationname,loc.latitude,loc.longitude);

    // set the value of some of the Data Reader parameters
    d->i = 0;
    d->ninputs = 1;
    d->ndata = 8760; // FIXME -- is a variable length file possible?
    d->nmaxoutputs = 7; // FIXME

    DATA(d)->rows = ASC_NEW_ARRAY(Tmy3Point,d->ndata);

	/* set the number of inputs and outputs */
	d->ninputs = 1;
	d->noutputs = 7;
	return 0;
}
예제 #9
0
uint32 var_interface(struct var_variable *var)
{
  struct Instance *c;
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return FALSE;
  }
  c = ChildByChar(IPTR(var->ratom),INTERFACE_V);
  if( c == NULL ) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"no 'interface' field");
    /* WriteInstance(stderr,IPTR(var->ratom)); */
    return 0;
  }
  var_set_flagbit(var,VAR_INTERFACE,GetBooleanAtomValue(c));
  return( GetIntegerAtomValue(c) );
}
예제 #10
0
char *var_make_name(const slv_system_t sys,const  struct var_variable *var){
	if(sys == NULL){
		ERROR_REPORTER_HERE(ASC_PROG_ERR,"called with NULL system.");
		return NULL;
	}
	return WriteInstanceNameString(IPTR(var->ratom),IPTR(slv_instance(sys)));
}
예제 #11
0
void var_set_relaxed(struct var_variable *var, uint32 fixed)
{
  struct Instance *c;
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return;
  }
  c = ChildByChar(IPTR(var->ratom),RELAXED_V);
  if( c == NULL ) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"no 'relaxed' field");
    /* WriteInstance(stderr,IPTR(var->ratom)); */
    return;
  }
  SetBooleanAtomValue(c,fixed,(unsigned)0);
  var_set_flagbit(var,VAR_RELAXED,fixed);
}
예제 #12
0
uint32 var_relaxed(struct var_variable *var)
{
  struct Instance *c;
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
	return FALSE;
  }
  c = ChildByChar((var->ratom),RELAXED_V);
  if( c == NULL ) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"no 'relaxed' field");
    /* WriteInstance(stderr,(var->ratom)); */
    return FALSE;
  }
  var_set_flagbit(var,VAR_RELAXED,GetBooleanAtomValue(c));
  return( GetBooleanAtomValue(c) );
}
예제 #13
0
/**
	Checks sys for NULL and for integrity.
*/
static int check_system(slvDOF_system_t sys){
	if(sys == NULL) {
		ERROR_REPORTER_HERE(ASC_PROG_ERR,"system is NULL");
	    return 1;
	}

	switch(sys->integrity) {
	case OK:
	    return 0;
	case DESTROYED:
	    ERROR_REPORTER_HERE(ASC_PROG_ERR,"system was recently destroyed");
	    return 1;
	default:
		ERROR_REPORTER_HERE(ASC_PROG_ERR,"system reused or never allocated");
	    return 1;
	}
}
예제 #14
0
void var_set_value(struct var_variable *var, real64 value)
{
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return;
  }
  SetRealAtomValue(var->ratom,value,(unsigned)0);
}
예제 #15
0
real64 var_value(const struct var_variable *var)
{
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var (%s)",(var==NULL?"null":"null ratom"));
    return 0.0;
  }
  return( RealAtomValue(var->ratom) );
}
예제 #16
0
void var_set_sindexF(struct var_variable *var, int32 sindex)
{
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return;
  }
  var->sindex = sindex;
}
예제 #17
0
int32 var_sindexF(const struct var_variable *var)
{
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return -1;
  }
  return var->sindex;
}
예제 #18
0
extern uint32 var_flagbit(const struct var_variable *var,const uint32 one)
{
  if (var==NULL || var->ratom == NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return 0;
  }
  return (var->flags & one);
}
예제 #19
0
int datareader_tmy3_eof(DataReader *d){
	parse *p = DATA(d)->p;
	if(parseEnd(p)){
		CONSOLE_DEBUG("REACHED END OF FILE");
		if(d->i < d->ndata){
			ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Incomplete data set found (%d rows < %d expected",d->i, d->ndata);
		}
		d->ndata=d->i;
		int i;double tmin = +0.5*DBL_MAX,tmax = -0.5*DBL_MAX;
		for(i=0; i<d->ndata; ++i){
			double t = DATA(d)->rows[i].t;
			if(t < tmin)tmin = t;
			if(t > tmax)tmax = t;
		}
		ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Read %d rows, t in range [%f,%f] d",d->ndata,tmin/3600./24.,tmax/3600./24.);
		return 1; /* yes, end of file */
	}
	return 0; /* no, more data still */
}
예제 #20
0
static int32 ipopt_destroy(slv_system_t server, SlvClientToken asys){
	IpoptSystem *sys;
	UNUSED_PARAMETER(server);
	sys = SYS(asys);
	slv_destroy_parms(&(sys->p));
	if(sys->s.cost) ascfree(sys->s.cost);
	ASC_FREE(sys);
	ERROR_REPORTER_HERE(ASC_PROG_WARNING,"ipopt_destroy still needs debugging");
	return 0;
}
예제 #21
0
int32 var_apply_filter(const struct var_variable *var,
	const var_filter_t *filter
){
  if (var==NULL || filter==NULL || var->ratom == NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"miscalled with NULL");
    return FALSE;
  }
  /* AND to mask off irrelevant bits in flags and match value, then compare */
  return (filter->matchbits & var->flags) == (filter->matchbits & filter->matchvalue);
}
예제 #22
0
/**
	This function is called when the black-box relation is being instantiated.

	This just gets the data member and checks that it's valid, and stores
	it in the blackbox data field.
*/
static int sunpos_nrel_prepare(struct BBoxInterp *bbox,
	   struct Instance *data,
	   struct gl_list_t *arglist
){
	struct Instance *inst;
	double latitude, longitude, elevation;

	/* get the latitude */
	GET_CHILD_VAL(latitude);
	CONSOLE_DEBUG("Latitude: %0.3f",latitude);
	if(latitude > PI/2 || latitude < -PI/2){
		ERROR_REPORTER_HERE(ASC_USER_ERROR,"'latitude' is out of allowable range -PI/2 to PI/2.");
		return 1;
	}

	/* get the longitude */
	GET_CHILD_VAL(longitude);
	CONSOLE_DEBUG("Longitude: %0.3f",longitude);
	if(longitude > PI || longitude < -PI){
		ERROR_REPORTER_HERE(ASC_USER_ERROR,"'latitude' is out of allowable range -PI to PI.");
		return 1;
	}

	/* get the elevation */
	GET_CHILD_VAL(elevation);
	CONSOLE_DEBUG("Elevation: %0.3f m",elevation);
	if(elevation < -6500000){
		ERROR_REPORTER_HERE(ASC_USER_ERROR,"'elevation' is out of allowable range (must be > -6,500 km)");
		return 1;
	}

	spa_data *S = ASC_NEW(spa_data);
	S->latitude = latitude * 180/PI;
	S->longitude = longitude * 180/PI;
	S->elevation = elevation;
	S->function = SPA_ZA_JD;

	ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Prepared position for sun position.\n");
	bbox->user_data = (void *)S;
	return 0;
}
예제 #23
0
void var_set_flagbit(struct var_variable *var, uint32 field,uint32 one)
{
  if (var==NULL || var->ratom == NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return;
  }
  if (one) {
    var->flags |= field;
  } else {
    var->flags &= ~field;
  }
}
예제 #24
0
/**
	ASCEND external evaluation function
	Outputs: mu, k, rho, cp
	Inputs: p, T
	@return 0 on success
*/
int mukrhocp_pT_calc(struct BBoxInterp *bbox,
		int ninputs, int noutputs,
		double *inputs, double *outputs,
		double *jacobian
){
	(void)bbox; (void)jacobian; // not used
	(void)ninputs; (void)noutputs; // not used currently

	// convert inputs to freesteam dimensionful values
	double p = inputs[0]; /* ASCEND uses SI units, so no conversion needed */
	double T = inputs[1]; /* ASCEND uses SI units, so no conversion needed */

#ifdef BBOX_DEBUG
	ERROR_REPORTER_HERE(ASC_USER_NOTE,
		"Evaluating with p = %f bar, T = %f K = %f C"
		,p,T,T-273.15
	);
#endif

	SteamState S;
	S = freesteam_set_pT(p,T);
	double mu = freesteam_mu(S);
	double k = freesteam_k(S);
	double rho = freesteam_rho(S);
	double cp = freesteam_cp(S);

#ifdef BBOX_DEBUG
	ERROR_REPORTER_HERE(ASC_USER_NOTE,
		"Got mu = %f, k = %f, rho = %f, cp = %f"
		, mu, k, rho, cp
	);
#endif

	outputs[0] = mu;
	outputs[1] = k;
	outputs[2] = rho;
	outputs[3] = cp;

	return 0;
}
예제 #25
0
/**
	This is the function called from "IMPORT heatex_pinch"

	It sets up the functions contained in this external library, but doesn't
	actually create any specific external-relation instances.
*/
extern
ASC_EXPORT int heatex_pinch_register(){
	int result = 0;

	ERROR_REPORTER_HERE(ASC_USER_WARNING,"HEATEX is still EXPERIMENTAL.\n");

	result += CreateUserFunctionBlackBox("heatex_DT_phmphmQ"
		,heatex_prepare
		,heatex_calc
		,(ExtBBoxFunc*)NULL /* no derivatives yet */
		,(ExtBBoxFunc*)NULL /* hessian not provided yet */ \
		,(ExtBBoxFinalFunc*)NULL /* finalisation not implemented */ \
		,7, 1 /* inputs, outputs */
		,heatex_help
		,0.0
	);

	if(result){
		ERROR_REPORTER_HERE(ASC_PROG_NOTE,"result = %d\n",result);
	}
	return result;
}
예제 #26
0
real64 var_nominal(struct var_variable *var)
{
  struct Instance *c;
  if (var==NULL || var->ratom==NULL) {
    ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad var");
    return 1.0;
  }
  c = ChildByChar(var->ratom,NOMINAL_V);
  if( c == NULL ) {
    FPRINTF(ASCERR,"no 'nominal' field in variable");
    /* WriteInstance(stderr,IPTR(var->ratom)); */
    return 1.0;
  }
  return( RealAtomValue(c) );
}
예제 #27
0
static int32 ipopt_eligible_solver(slv_system_t server){
	struct rel_relation **rp;
	struct var_variable **vp;
	rel_filter_t rfilt;
	var_filter_t vfilt;

	rfilt.matchbits = (REL_CONDITIONAL |  REL_INWHEN);
	rfilt.matchvalue = (REL_CONDITIONAL | REL_INWHEN);

	vfilt.matchbits = (VAR_BINARY);
	vfilt.matchvalue = (VAR_BINARY);

	/// @todo check that there is a MAXIMIZE or MINIMIZE statement
	if (slv_get_obj_relation(server) == NULL)
		ERROR_REPORTER_HERE(ASC_USER_ERROR,"No objective function found");

	/// @todo check that there are no WHENs or CONDITIONALs
	for( rp=slv_get_solvers_rel_list(server); *rp != NULL ; ++rp ) {
		if(rel_apply_filter(*rp,&rfilt)){
			ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"WHEN and CONDITIONAL Statements are not supported.");
			return(FALSE);
		}
	}

	/// @todo check that there are no discrete-valued variables
	for( vp=slv_get_solvers_var_list(server); *vp != NULL ; ++vp ) {
		if(var_apply_filter(*vp,&vfilt)){
			ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Discrete Variables not supported.");
			return(FALSE);
		}
	}

	/// @todo check anything else?

	return 1;
}
예제 #28
0
/**
   'heatex_prepare' just gets the stream details and the number of slices for
	internal calculation, and checks that the stream names are valid in FPROPS.

	TODO FIXME allow general support for specification of components, incl type,source.
*/
int heatex_prepare(struct BBoxInterp *bbox,
	   struct Instance *data,
	   struct gl_list_t *arglist
){
	HeatExData *hxd = ASC_NEW(HeatExData);
	if(!hxd)goto fail;

	struct Instance *compinst[2], *ninst;
	const char *comp[2];

	N_SYM = AddSymbol("n");
	/* we look through these 0,1 below */
	heatex_symbols[0] = AddSymbol("component");
	heatex_symbols[1] = AddSymbol("component_hot");

	ninst = ChildByChar(data,N_SYM);
	if(!ninst){
		ERROR_REPORTER_HERE(ASC_USER_ERROR
			,"Couldn't locate '%s' in DATA, please check usage.",SCP(N_SYM)
		);
		goto fail;
	}
	if(InstanceKind(ninst)!=INTEGER_CONSTANT_INST){
		ERROR_REPORTER_HERE(ASC_USER_ERROR,"DATA member '%s' must be a symbol_constant",SCP(N_SYM));
		goto fail;
	}
	hxd->n = IC_INST(ninst)->value;

	int i;
	for(i=0;i<2;++i){
		/* get the component names for cold and hot sides */
		compinst[i] = ChildByChar(data,heatex_symbols[i]);
		if(!compinst[i]){
			ERROR_REPORTER_HERE(ASC_USER_ERROR
				,"Couldn't locate '%s' in DATA, please check usage."
				,SCP(heatex_symbols[i])
			);
			goto fail;
		}
		if(InstanceKind(compinst[i])!=SYMBOL_CONSTANT_INST){
			ERROR_REPORTER_HERE(ASC_USER_ERROR,"DATA member '%s' must be a symbol_constant",SCP(heatex_symbols[i]));
			goto fail;
		}
		comp[i] = SCP(SYMC_INST(compinst[i])->value);
		CONSOLE_DEBUG("%s: %s",SCP(heatex_symbols[i]),comp[i]);
		if(comp[i]==NULL || strlen(comp[i])==0){
			ERROR_REPORTER_HERE(ASC_USER_ERROR,"'%s' is NULL or empty",heatex_symbols[i]);
			goto fail;
		}

		hxd->comp[i] = fprops_fluid(comp[i], NULL,NULL);
		if(hxd->comp[i] == NULL){
			ERROR_REPORTER_HERE(ASC_USER_ERROR,"Heat exchanger %s name '%s' not recognised. Check list of supported species.",SCP(heatex_symbols[i]),comp[i]);
			goto fail;
		}
	}

	bbox->user_data = (void *)hxd;
	ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Heat exchanger data structure OK.\n",comp);
	return 0;

fail:
	if(hxd){
		/* TODO FIXME implement FPROPS freeing, will be important with fprops2 */
		//if(hxd->comp[0])ASC_FREE(hxd->comp[0]);
		//if(hxd->comp[1])ASC_FREE(hxd->comp[1]);
		ASC_FREE(hxd);
	}
	return 1;
}
예제 #29
0
/**
	Implementation function for var_n_incidences().  Do not call this
	function directly - use var_n_incidences() instead.
*/
int32 var_n_incidencesF(struct var_variable *var)
{
  if (var!=NULL) return var->n_incidences;
  ERROR_REPORTER_HERE(ASC_PROG_ERR,"NULL var");
  return 0;
}
예제 #30
0
/**
	ASCEND external evaluation function
	Outputs: T
	Inputs: p, h
	@return 0 on success
*/
int Tvsx_ph_calc(struct BBoxInterp *bbox,
		int ninputs, int noutputs,
		double *inputs, double *outputs,
		double *jacobian
){
	(void)bbox; (void)jacobian; // not used
	(void)ninputs; (void)noutputs; // not used currently

#if 0
	ASC_ASSERT(ninputs==2);
	ASSERT(noutputs==2);
#endif

	// convert inputs to freesteam dimensionful values
	double p = inputs[0]; /* ASCEND uses SI units, so no conversion needed */
	double h = inputs[1]; /* ASCEND uses SI units, so no conversion needed */

#ifdef BBOX_DEBUG
	ERROR_REPORTER_HERE(ASC_USER_NOTE,
		"Evaluating with p = %f bar, h = %f kJ/kg"
		,p,h
	);
#endif

	SteamState S;
	S = freesteam_set_ph(p,h);
	double T, dTdh_p, dTdp_h;
	double v, dvdh_p, dvdp_h;
	double s, dsdh_p, dsdp_h;

	double x, dxdh_p, dxdp_h;
	switch(bbox->task){
	case bb_func_eval:
		T = freesteam_T(S);
		v = freesteam_v(S);
		s = freesteam_s(S);
		if(S.region==3){
			/* nonsense value */
			x = 0.5;
		}else{
			x = freesteam_x(S);
		}

#ifdef BBOX_DEBUG
		ERROR_REPORTER_HERE(ASC_USER_NOTE,
			"Got result T = %f K"
			,T
		);
#endif
		outputs[0] = T;
		outputs[1] = v;
		outputs[2] = s;
		outputs[3] = x;

		/* TODO add error checks here, surely? */
		return 0;
	case bb_deriv_eval:
		/*fprintf(stderr,"derivative evaluation, region %d\n",S.region);*/
		dTdp_h = freesteam_deriv(S,"Tph");
		dTdh_p = freesteam_deriv(S,"Thp");
		dvdp_h = freesteam_deriv(S,"vph");
		dvdh_p = freesteam_deriv(S,"vhp");
		dsdp_h = freesteam_deriv(S,"sph");
		dsdh_p = freesteam_deriv(S,"shp");
		switch(S.region){
			case 4:
				dxdp_h = freesteam_deriv(S,"xph");
				dxdh_p = freesteam_deriv(S,"xhp");
				break;
			default:
				/* try to 'slope' the solver into the saturation region */
				dxdp_h = 0;
				dxdh_p = 0.001;
				break;
		}
#ifdef BBOX_DEBUG
		ERROR_REPORTER_HERE(ASC_USER_NOTE,
			"Got result (dT/dp)h = %g, (dT/dh)p = %g K/Pa",dTdp_h,dTdh_p
		);
		ERROR_REPORTER_HERE(ASC_USER_NOTE,
			"Got result (dv/dp)h = %g, (dv/dh)p = %g K/Pa",dvdp_h,dvdh_p
		);
#endif
		jacobian[0] = dTdp_h;
		jacobian[1] = dTdh_p;
		jacobian[2] = dvdp_h;
		jacobian[3] = dvdh_p;
		jacobian[4] = dsdp_h;
		jacobian[5] = dsdh_p;
		jacobian[6] = dxdp_h;
		jacobian[7] = dxdh_p;
		return 0;
	default:
		ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid call, unknown bbox->task");
		return 1;
	}
}