All Downloads are FREE. Search and download functionalities are using the official Maven repository.

decodes.cwms.validation.ScreeningCriteria Maven / Gradle / Ivy

Go to download

A collection of software for aggregatting and processing environmental data such as from NOAA GOES satellites.

The newest version!
/**
 * $Id$
 * 
 * Copyright 2015 U.S. Army Corps of Engineers, Hydrologic Engineering Center.
 * 
 * $Log$
 * Revision 1.10  2016/03/24 19:02:49  mmaloney
 * Refactoring for Python.
 *
 * Revision 1.9  2016/03/09 16:44:59  mmaloney
 * Improved Debug.
 *
 * Revision 1.8  2016/02/29 22:14:48  mmaloney
 * Fixed ROC checks. It was not using the correct flags mask.
 *
 * Revision 1.7  2015/11/12 15:17:13  mmaloney
 * Added HEC headers.
 *
 */
package decodes.cwms.validation;

import ilex.util.Logger;
import ilex.var.NamedVariable;
import ilex.var.NoConversionException;
import ilex.var.TimedVariable;
import ilex.var.Variable;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.function.Function;

import decodes.cwms.CwmsFlags;
import decodes.db.UnitConverter;
import decodes.tsdb.CTimeSeries;
import decodes.tsdb.DataCollection;
import decodes.tsdb.IntervalCodes;
import decodes.tsdb.IntervalIncrement;
import decodes.tsdb.TimeSeriesIdentifier;
import decodes.tsdb.VarFlags;
import decodes.tsdb.algo.AW_AlgorithmBase;

/**
 * Holds a collection of checks to perform on one or more
 * time series values, optionally during a specified season.
 */
public class ScreeningCriteria
{
	public static final String module = "ScreeningCriteria";
	
	/** Start of season for this group of checks, or null if all-time */
	Calendar seasonStart = null;
	
	/** Absolute value checks */
	ArrayList absChecks = new ArrayList();
	
	/** Constant value checks */
	ArrayList constChecks = new ArrayList();

	/** Rate-of-Change per Hour checks */
	ArrayList rocPerHourChecks = new ArrayList();
	
	/** Duration Magnitude checks */
	ArrayList durCheckPeriods = new ArrayList();
	
	/** variable to track validity as tests are executed */
	public final static char ValidityOK = 'O';
	public final static char ValidityQuestion = 'Q';
	public final static char ValidityReject = 'R';
	public final static char ValidityMissing = 'M';
	private char validity = ValidityOK;
	private int testbits = 0;
	
	private String estimateExpression = null;
	
	/** The owner of this criteria set */
	private Screening screening;
	
	public ScreeningCriteria(Calendar seasonStart)
	{
		this.seasonStart = seasonStart;
	}
	
	public ScreeningCriteria()
	{
		setSeasonStart(Calendar.JANUARY, 1);
	}

	public void addAbsCheck(AbsCheck absCheck)
	{
		absChecks.add(absCheck);
	}
	
	public void addConstCheck(ConstCheck constCheck)
	{
		constChecks.add(constCheck);
	}
	
	public void addRocPerHourCheck(RocPerHourCheck rocPerHourCheck)
	{
		rocPerHourChecks.add(rocPerHourCheck);
	}
	
	public void addDurCheckPeriod(DurCheckPeriod durCheckPeriod)
	{
		durCheckPeriods.add(durCheckPeriod);
	}

	/**
	 * This method does the actual work of the checks. The output value
	 * is written and the appropriate flag bits are set reflecting the
	 * results of the checks.
	 * @param dc The data collection from the computation processor
	 * @param input the input time series
	 * @param output the output time series
	 * @param dataTime the time-stamp of the input value being checked.
	 * @throws NoConversionException 
	 */
	public void executeChecks(DataCollection dc, CTimeSeries input,
		Date dataTime, NamedVariable output, AW_AlgorithmBase alg)
	{
		TimedVariable tv = input.findWithin(dataTime, alg.roundSec);
		if (tv == null)
			return;

		boolean setInputFlags = true;
		boolean noOverwrite = false;
		boolean noOutputOnReject = true;
		boolean setRejectMissing = true;
		boolean inputIsOutput = true;
		if (alg instanceof ScreeningAlgorithm)
		{
			ScreeningAlgorithm scrAlg = (ScreeningAlgorithm)alg;
			setInputFlags = scrAlg.setInputFlags;
			noOverwrite = scrAlg.noOverwrite;
			noOutputOnReject = scrAlg.noOutputOnReject;
			setRejectMissing = scrAlg.setRejectMissing;
			inputIsOutput = scrAlg.inputIsOutput();
		}
		
		// MJM 20121002 Leave protected values completely unchanged. Don't even validate.
		if (inputIsOutput && (tv.getFlags() & CwmsFlags.PROTECTED) != 0)
		{
			alg.debug1("Skipping PROTECTED value at " + alg.debugSdf.format(dataTime));
			return;
		}
		
		double v = 0.0;
		try { v = tv.getDoubleValue(); }
		catch(NoConversionException ex)
		{
			alg.warning("Crit-1: " + ex.toString());
			return;
		}
		
		output.setValue(v);
		int flags = tv.getFlags();
		
		alg.debug3("Executing checks, value " + v + " at time " + 
			alg.debugSdf.format(dataTime)
			+ ", flags initially=0x" + Integer.toHexString(flags));

		flags |= doChecks(dc, input, dataTime, alg, v);
		
		int mask = CwmsFlags.TEST_MASK|CwmsFlags.VALIDITY_MASK;
		// MJM 20121002 If input & output are the same time series
		// and the flags are unchanged. Then do nothing.
		if (inputIsOutput)
		{
			if ((flags&mask) != (tv.getFlags()&mask))
				// Flags are changed as a result of validation.
			{
				alg.clearFlagBits(output, CwmsFlags.VALIDITY_MASK | CwmsFlags.TEST_MASK);
				alg.setFlagBits(output, flags);
			}
		}
		else // output is different from input.
		{
			// The 'setInputFlags' means that, in addition to setting output,
			// set the flag bits on the input param. Thus it is only used when
			// output and input are different time series.
			// Also, it must only be done if the flags have changed to avoid infinite loop.
			if (setInputFlags && (flags&mask) != (tv.getFlags()&mask))
				alg.setInputFlagBits("input", flags, mask);
			
			// Set NO_OVERWRITE if the property is set in the comp.
			if (noOverwrite)
				flags |= VarFlags.NO_OVERWRITE;
			
			// The 'setRejectedMissing' property means that, if the result of the
			// checks is that the value is 'REJECTED', then in the output, set it as MISSING.
			// It is typically used along with setInputFlags. The input value gets the 'REJECTED'
			// flag, where the output gets flagged as MISSING.
			if ((flags & CwmsFlags.VALIDITY_MASK) == CwmsFlags.VALIDITY_REJECTED)
			{
				if (noOutputOnReject)
				{
					return; // Simply don't write output if rejected.
				}
				else if (setRejectMissing)
				{
					flags = (flags & (~CwmsFlags.VALIDITY_MASK))
						  | CwmsFlags.VALIDITY_MISSING;
				}
			}
			alg.clearFlagBits(output, mask);
			alg.setFlagBits(output, flags);
		}
	}
	
	/**
	 * Perform the checks and return the flag results
	 * @param dc the data collection
	 * @param input the input time series
	 * @param dataTime the time of the value to check
	 * @param alg The algorithm executive (for log messages with context)
	 * @return the flag results
	 */
	public int doChecks(DataCollection dc, CTimeSeries input,
		Date dataTime, AW_AlgorithmBase alg, double value)
	{
//		TimedVariable tv = input.findWithin(dataTime, alg.roundSec);
//		if (tv == null)
//			throw new NoSuchObjectException(module + ".doChecks - no value at time "
//				+ alg.debugSdf.format(dataTime));
//		double value = 0.0;
//		try { value = tv.getDoubleValue(); }
//		catch(NoConversionException ex)
//		{
//			throw new NoSuchObjectException(module + ".doChecks - Value at time "
//				+ alg.debugSdf.format(dataTime) + " is not a number: " + tv.toString());
//		}
		
		TimeSeriesIdentifier inputTsid = input.getTimeSeriesIdentifier();
		IntervalIncrement tsinc = IntervalCodes.getIntervalCalIncr(inputTsid.getInterval());
		boolean inputIrregular = tsinc == null || tsinc.getCount() == 0;

		int resultFlags = CwmsFlags.SCREENED;
		validity = ValidityOK;
		testbits = 0;

		// ABS checks
		if (screening == null || screening.isRangeActive())
			for(AbsCheck chk : absChecks)
			{
				alg.debug1(chk.toString());
				if (compare(value, chk.getLow()) < 0 || compare(value,chk.getHigh()) > 0)
				{
					setValidity(chk.getFlag(), CwmsFlags.TEST_ABSOLUTE_VALUE);
					alg.info(input.getTimeSeriesIdentifier().getUniqueString()
						+ " value " + value + " at time " 
						+ alg.debugSdf.format(dataTime)
						+ " failed " + chk.toString());
				}
			}
		
		// CONST checks
		Calendar aggCal = alg.aggCal;
		if (screening == null || screening.isConstActive())
		{
		nextConstCheck:
			for(ConstCheck chk : constChecks)
			{
				alg.debug1(chk.toString());
				
				// Flag if value has not changed more than tolerance
				// over specified duration.
				// Abort check if more than allowedMissing are not present.
				IntervalIncrement durinc = IntervalCodes.getIntervalCalIncr(chk.getDuration());
				
				double minvalue = Double.POSITIVE_INFINITY;
				double maxvalue = Double.NEGATIVE_INFINITY;
				aggCal.setTime(dataTime);
				aggCal.add(durinc.getCalConstant(), -durinc.getCount());
				Date startTime = aggCal.getTime();
	
				// Compute the maximum allowable time gap in seconds.
				// For regular inputs, this is expressed as nmiss * the interval of the input
				IntervalIncrement maxGap = null;
				if (!inputIrregular)
				{
					if (chk.getAllowedMissing() > 0)
						maxGap = new IntervalIncrement(tsinc.getCalConstant(), 
							tsinc.getCount() * chk.getAllowedMissing());
					
				}
				else // maxGap can be specified in the DATCHK files
					maxGap = chk.getMaxGap();
				
				// find the earliest index in the input TS >= aggCal. Then increment through CTS
				// until !d.after(dataTime)
				if (input.size() < 1)
					continue nextConstCheck;
				alg.debug3("Iterating for CONST check, maxGap=" + maxGap);
				int idx = 0;
				Date lastTime = null, firstTime = null;
				for(; idx < input.size() && input.sampleAt(idx).getTime().before(startTime); idx++);
				
				// For irregular, we may need to backup to first sample before the start of period.
				if (input.sampleAt(idx).getTime().after(startTime) && idx > 0)
					idx--;
				for(; idx < input.size() && !input.sampleAt(idx).getTime().after(dataTime); idx++)
				{
					TimedVariable x = input.sampleAt(idx);
					
					double xv = 0.0;
					try { xv = x.getDoubleValue(); }
					catch(NoConversionException ex)
					{
						continue; // treat as missing.
					}
					
					if (firstTime == null)
						firstTime = x.getTime();
	//alg.debug3("    CONST value=" + xv + " at time " + alg.debugSdf.format(x.getTime()));
	
					if (maxGap != null && lastTime != null)
					{
						aggCal.setTime(lastTime);
						aggCal.add(maxGap.getCalConstant(), maxGap.getCount());
						if (aggCal.getTime().before(x.getTime()))
						{
							alg.debug1("Value skipped because of time gap: "
								+ alg.debugSdf.format(lastTime) + " - " + alg.debugSdf.format(x.getTime()));
							lastTime = x.getTime();
							continue nextConstCheck;
						}
					}
					
					if (xv < minvalue)
						minvalue = xv;
					if (xv > maxvalue)
						maxvalue = xv;
					lastTime = x.getTime();
				}
				
				// If variance is less than the tolerance, flag the value.
				double variance = maxvalue - minvalue;
	//alg.debug3("  Iteration done, max=" + maxvalue + ", min=" + minvalue + ", variance=" + variance);
				if (compare(variance, chk.getTolerance()) <= 0)
				{
					// Make sure that specified duration was exceeded.
					aggCal.setTime(firstTime);
					aggCal.add(durinc.getCalConstant(), durinc.getCount());
					if (!lastTime.before(aggCal.getTime()))
					{
						setValidity(chk.getFlag(), CwmsFlags.TEST_CONSTANT_VALUE);
						alg.info(input.getTimeSeriesIdentifier().getUniqueString()
							+ " value " + value + " at time " + alg.debugSdf.format(dataTime)
							+ " failed " + chk.toString()
							+ " max=" + maxvalue + ", min=" + minvalue);
					}
	//else alg.debug3("No flag set because duration was not seen.");
				}
	//else alg.debug3("No flag set because variance exceeded.");
			}
		}
		
		if (screening != null && !screening.isRocActive())
		alg.debug1("Skipping ROC checks because ROC is not active.");
		
		if (screening == null || screening.isRocActive())
		{
			// RATE checks
			TimedVariable prevtv = input.findWithin(
				new Date(dataTime.getTime()-3600000L), alg.roundSec);
			if (prevtv != null 
			 && (prevtv.getFlags() & CwmsFlags.FLAG_MISSING_OR_REJECTED) == 0)
			{
				for(RocPerHourCheck chk : rocPerHourChecks)
				{
					alg.debug1(chk.toString());
					try
					{
						double delta = value - prevtv.getDoubleValue();
						if (compare(delta, chk.getFall()) < 0 || compare(delta,chk.getRise()) > 0)
						{
							setValidity(chk.getFlag(), CwmsFlags.TEST_RATE_OF_CHANGE);
							alg.info(input.getTimeSeriesIdentifier().getUniqueString()
								+ " value " + value + " at time " + alg.debugSdf.format(dataTime)
								+ " failed " + chk.toString()
								+ " prev=" + prevtv.getDoubleValue() 
								+ ", prevFlags=0x" + Integer.toHexString(prevtv.getFlags()) + ", delta=" + delta);
						}
					}
					catch(NoConversionException ex)
					{
						alg.warning("Crit-3: " + ex.toString());
						continue;
					}
				}
			}
			else if (rocPerHourChecks.size() > 0) 
				alg.debug1("Not checking dataTime=" + dataTime + " because prev is missing or rejected.");
				
		}
		
		if (screening == null || screening.isDurMagActive())
		{
			// DUR checks
			// For Duration-Magnitude tests, first figure out what kind of 
			// accumulation. If input duration is not 0 then we have periodic
			// incremental numbers -- just add them. If duration
			// IS 0 then we have a cumulative number and we have to take the delta.
			String tsDur = inputTsid.getPart("duration");
			IntervalIncrement tsDurCalInc = IntervalCodes.getIntervalCalIncr(tsDur);
			boolean incremental = tsDurCalInc != null && tsDurCalInc.getCount() > 0;
			
			for(DurCheckPeriod chk : durCheckPeriods)
			{
				alg.debug1(chk.toString() + ", incremental=" + incremental);
				
				// Accumulate change over duration specified in the DATCHK file.
				IntervalIncrement durinc = IntervalCodes.getIntervalCalIncr(chk.getDuration());
				if (durinc.getCount() == 0)
					continue;
	//alg.debug3("   DUR period=" + tsinc);
				
				double prev = Double.NEGATIVE_INFINITY;
				double tally = 0;
				aggCal.setTime(dataTime);
				aggCal.add(durinc.getCalConstant(), -durinc.getCount());
				Date startTime = aggCal.getTime();
				
				// find the earliest index in the input TS >= aggCal. Then increment through CTS
				// until !d.after(dataTime)
				int idx = 0;
				for(; idx < input.size() && input.sampleAt(idx).getTime().before(startTime); idx++);
				if (!incremental && input.sampleAt(idx).getTime().after(startTime) && idx > 0)
					idx--;
				for(; idx < input.size() && !input.sampleAt(idx).getTime().after(dataTime); idx++)
				{
					TimedVariable x = input.sampleAt(idx);
					double xv = 0;
					try { xv = x.getDoubleValue(); }
					catch(NoConversionException ex)
					{
						alg.warning("Crit-4: " + ex.toString());
						continue;
					}
	//alg.debug3("   DUR sample[" + idx + "] time=" + alg.debugSdf.format(x.getTime()) + ", value=" + xv);
					if (!incremental)
					{
						// Must take deltas of cumulative values and then add them.
						if (prev != Double.NEGATIVE_INFINITY)
						{
							double delta = xv - prev;
							tally += delta;
						}
						prev = xv;
					}
					else // Already have incremental numbers, just add them.
						tally += xv;
	//alg.debug3("    Tally is now " + tally);
				}
	//alg.debug3("  Looping done, tally=" + tally + ", low=" + chk.getLow() + ", high=" + chk.getHigh());
				if (compare(tally,chk.getLow()) < 0 || compare(tally,chk.getHigh()) > 0)
				{
					setValidity(chk.getFlag(), CwmsFlags.TEST_DURATION_VALUE);
					alg.info(input.getTimeSeriesIdentifier().getUniqueString()
						+ " value " + value + " at time " + alg.debugSdf.format(dataTime)
						+ " failed " + chk.toString() + ", tally=" + tally
						+ "limits=(" + chk.getLow() + "," + chk.getHigh());
				}
	//else alg.debug3("  Not out of limits.");
			}
		}
		
		switch(validity)
		{
		case ValidityOK: resultFlags |= CwmsFlags.VALIDITY_OKAY; break;
		case ValidityQuestion: resultFlags |= CwmsFlags.VALIDITY_QUESTIONABLE; break;
		case ValidityReject: resultFlags |= CwmsFlags.VALIDITY_REJECTED; break;
		case ValidityMissing: resultFlags |= CwmsFlags.VALIDITY_MISSING; break;
		}
		resultFlags |= testbits;
		
		alg.debug1("After all checks, value " + value + " at time " + 
			alg.debugSdf.format(dataTime)
			+ ", resultFlags = 0x" + Integer.toHexString(resultFlags));
		
		return resultFlags;
	}
	
	/**
	 * Compare value but only to 3 decimal places
	 * @return -1 if value is < limit, 0 if value==limit, 1 if value > limit
	 */
	private int compare(double value, double limit)
	{
		long lval = (long)(value*1000 + .5);
		long llim = (long)(limit*1000 + .5);
		long diff = lval - llim;
		return diff < 0 ? -1 : diff > 0 ? 1 : 0;
//		double x = value - limit;
//		int sign = (x < 0) ? -1 : 1;
//		if (x < 0) x = -x;
//		long diff = (long)(x*1000.0 + .5) * sign;
//		return diff < 0 ? -1 : diff > 0 ? 1 : 0;
	}
	
	private void setValidity(char testFlag, int testbits)
	{
		switch(testFlag)
		{
		case ValidityQuestion:
			if (validity != ValidityMissing && validity != ValidityReject)
			{
				validity = ValidityQuestion;
				this.testbits |= testbits;
			}
			break;
		case ValidityReject:
			if (validity != ValidityMissing)
			{
				validity = ValidityReject;
				this.testbits |= testbits;
			}
			break;
		case ValidityMissing:
			validity = ValidityMissing;
			this.testbits |= testbits;
			break;
		}
	}
	
	public void fillTimesNeeded(CTimeSeries inTS, TreeSet needed,
		Calendar aggCal, AW_AlgorithmBase alg)
	{
		TimeSeriesIdentifier tsid = inTS.getTimeSeriesIdentifier();
		IntervalIncrement tsinc = 
			IntervalCodes.getIntervalCalIncr(tsid.getInterval());
		boolean inputIrregular = tsinc == null || tsinc.getCount() == 0;

		// Note: AbsChecks don't need any historical data
		
		// ConstChecks will need the range from t-duration through t.
		for(ConstCheck cc : constChecks)
		{
			alg.debug3("Adding dates for const check: " + cc.toString());
			addDatesThruDuration(inTS, tsinc, 
				IntervalCodes.getIntervalCalIncr(cc.getDuration()),
				aggCal,needed, alg, inputIrregular);
		}

		// ROC checks need the previous hour for each trigger value
		if (rocPerHourChecks.size() > 0)
		{
			alg.debug3("Adding dates for ROC checks");

			// With the changes to addToNeeded, the following works fine for regular and irregular.
			// Although, 1 hour deltas are an odd thing for irregular.
			for(int idx = 0; idx < inTS.size(); idx++)
			{
				TimedVariable tv = inTS.sampleAt(idx);
				if (!VarFlags.wasAdded(tv))
					continue;
				// Get this sample's time and subtract duration
				Date sampleTime = tv.getTime();
				aggCal.setTime(sampleTime);
				aggCal.add(Calendar.HOUR_OF_DAY, -1);
				Date d = aggCal.getTime();
				addToNeeded(d, needed, inputIrregular);
			}
		}
		
		//DurCheckPeriod also needs the range
		for(DurCheckPeriod cc : durCheckPeriods)
		{
			alg.debug3("Adding dates for dur check: " + cc.toString());
			addDatesThruDuration(inTS, tsinc, 
				IntervalCodes.getIntervalCalIncr(cc.getDuration()),
				aggCal,needed, alg, inputIrregular);
		}

		// Finally remove from 'needed' anything we already have
		if (!inputIrregular)
		{
			// Don't do this if inputIrregular -- there should only be two dates and we need them both.
			alg.debug3("Removing needed dates we already have. needed.size=" + needed.size());
			for(Iterator dit = needed.iterator(); dit.hasNext(); )
			{
				Date d = dit.next();
				if (inTS.findWithin(d, alg.roundSec) != null)
					dit.remove();
			}
		}
		alg.debug1("ScreeningCriteria.fillTimesNeeded done.");
	}

	public Calendar getSeasonStart()
	{
		return seasonStart;
	}
	
	private void addDatesThruDuration(CTimeSeries inTS,
		IntervalIncrement tsinc, IntervalIncrement durinc, Calendar aggCal,
		TreeSet needed, AW_AlgorithmBase alg, boolean inputIrregular)
	{
		int nProcessed = 0;
		int nAdded = 0;
		for(int idx = 0; idx < inTS.size(); idx++)
		{
			TimedVariable tv = inTS.sampleAt(idx);
			if (!VarFlags.wasAdded(tv))
				continue;
			nProcessed++;
			// Get this sample's time and subtract duration
			Date sampleTime = tv.getTime();
			aggCal.setTime(sampleTime);
			aggCal.add(durinc.getCalConstant(), -durinc.getCount());
			Date d = aggCal.getTime();
			if (inputIrregular)
			{
				addToNeeded(d, needed, inputIrregular);
				addToNeeded(sampleTime, needed, inputIrregular);
				alg.debug3("addDatesThruDuration (irregular input) first=" + needed.first()
					+ ", last=" + needed.last());
			}
			else // Regular input, iterate forward by interval to get discrete times we need.
			{
				while(d.before(sampleTime))
				{
					if (inTS.findWithin(d, 5) == null)
					{
						addToNeeded(d, needed, inputIrregular);
						nAdded++;
					}
					aggCal.add(tsinc.getCalConstant(), tsinc.getCount());
					d = aggCal.getTime();
				}
				alg.debug3("addDatesThruDuration inTS.size="
					+ inTS.size() + ", nProcessed=" + nProcessed
					+ ", nAdded=" + nAdded);
			}
		}
	}
	
	private void addToNeeded(Date d, TreeSet needed, boolean inputIrregular)
	{
		if (inputIrregular)
		{
			int sz = needed.size();
			if (sz < 2)
				needed.add(d);
			else // there are already 2 distinct Date objects in the list
			{
				if (d.before(needed.first()))
				{
					needed.remove(needed.first());
					needed.add(d);
				}
				else if (d.after(needed.last()))
				{
					needed.remove(needed.last());
					needed.add(d);
				}
				// else it's in the middle of the range. Do nothing.
			}
		}
		else // We are accumulating a set of distinct dates.
			needed.add(d);
	}

	public ArrayList getDurCheckPeriods()
	{
		return durCheckPeriods;
	}
	
	public AbsCheck getAbsCheckFor(char flag)
	{
		for(AbsCheck ac : absChecks)
			if (ac.getFlag() == flag)
				return ac;
		return null;
	}
	
	public RocPerHourCheck getRocCheckFor(char flag)
	{
		for(RocPerHourCheck rocc : rocPerHourChecks)
			if (rocc.getFlag() == flag)
				return rocc;
		return null;
	}
	
	public ConstCheck getConstCheckFor(char flag)
	{
		for(ConstCheck cc : constChecks)
			if (cc.getFlag() == flag)
				return cc;
		return null;
	}

	public String getEstimateExpression()
	{
		return estimateExpression;
	}

	public void setEstimateExpression(String estimateExpression)
	{
		this.estimateExpression = estimateExpression;
	}

	public void setScreening(Screening screening)
	{
		this.screening = screening;
	}

	/**
	 * Set season start.
	 * @param monthConst - One of the MONTH constants defined in Calendar.
	 * @param day - the day of the month (1 = first day of month).
	 */
	public void setSeasonStart(int monthConst, int day)
	{
		if (seasonStart == null)
			seasonStart = Calendar.getInstance();
		seasonStart.set(Calendar.MONTH, monthConst);
		seasonStart.set(Calendar.DAY_OF_MONTH, day);
		seasonStart.set(Calendar.HOUR, 0);
		seasonStart.set(Calendar.MINUTE, 0);
		seasonStart.set(Calendar.SECOND, 0);
		seasonStart.set(Calendar.MILLISECOND, 0);
	}

	/**
	 * Converts units in this criteria object to the specified units using
	 * the passed converter.
	 * @param paramUnits
	 * @param uc
	 */
	public void convertUnits(String paramUnits, UnitConverter uc)
	{
		String what = "";
		try
		{
			for(AbsCheck ac : absChecks)
			{
				what = "abs " + ac.getFlag() + " high";
				if (ac.getHigh() != Double.POSITIVE_INFINITY)
					ac.setHigh(uc.convert(ac.getHigh()));
				what = "abs " + ac.getFlag() + " low";
				if (ac.getLow() != Double.NEGATIVE_INFINITY)
					ac.setLow(uc.convert(ac.getLow()));
			}
			for(RocPerHourCheck rc : rocPerHourChecks)
			{
				what = "roc " + rc.getFlag() + " rise";
				if (rc.getRise() != Double.POSITIVE_INFINITY)
					rc.setRise(uc.convert(rc.getRise()));
				what = "roc " + rc.getFlag() + " fall";
				if (rc.getFall() != Double.NEGATIVE_INFINITY)
					rc.setFall(uc.convert(rc.getFall()));
			}
			for(ConstCheck cc : constChecks)
			{
				what = "const " + cc.getFlag() + " tol";
				cc.setTolerance(uc.convert(cc.getTolerance()));
				what = "const " + cc.getFlag() + " min";
				cc.setMinToCheck(uc.convert(cc.getMinToCheck()));
			}
			for(DurCheckPeriod dcp : durCheckPeriods)
			{
				what = "durmag " + dcp.getFlag() + " high";
				if (dcp.getHigh() != Double.POSITIVE_INFINITY)
					dcp.setHigh(uc.convert(dcp.getHigh()));
				what = "durmag " + dcp.getFlag() + " low";
				if (dcp.getLow() != Double.NEGATIVE_INFINITY)
					dcp.setLow(uc.convert(dcp.getLow()));
			}
		}
		catch(Exception ex)
		{
			Logger.instance().warning("Screening '" + screening.getScreeningName()
				+ "' " + what + ": cannot convert to " + paramUnits + ": " + ex);
		}
	
		
	}

	public void clearChecks()
	{
		absChecks.clear();
		constChecks.clear();
		rocPerHourChecks.clear();
		durCheckPeriods.clear();
	}

	protected boolean hasAbs()
	{
		Function check = (AbsCheck c) ->
		{
			return c != null
					&& (c.getHigh() != Double.NEGATIVE_INFINITY
					|| c.getLow() != Double.NEGATIVE_INFINITY);
		};
		AbsCheck rejected = getAbsCheckFor('R');
		AbsCheck question = getAbsCheckFor('Q');
		return (check.apply(rejected) || check.apply(question));
	}
	protected boolean hasConst()
	{
		Function check = (ConstCheck c) ->
		{
			return c != null
					&& (c.getTolerance() != Double.NEGATIVE_INFINITY
					|| c.getAllowedMissing() != Integer.MIN_VALUE
					|| c.getMinToCheck() != Double.NEGATIVE_INFINITY);
		};
		ConstCheck reject = getConstCheckFor('R');
		ConstCheck question = getConstCheckFor('Q');
		return check.apply(reject) || check.apply(question);
	}

	protected boolean hasRoc()
	{
		Function check = (RocPerHourCheck c) ->
		{
			return c != null
					&& (c.getFall() != Double.NEGATIVE_INFINITY
					|| c.getRise() != Double.NEGATIVE_INFINITY);
		};
		RocPerHourCheck reject = getRocCheckFor('R');
		RocPerHourCheck question = getRocCheckFor('Q');
		return check.apply(reject) || check.apply(question);
	}

	protected boolean hasDurMag()
	{
		Function check = (DurCheckPeriod c) ->
		{
			return c != null
					&& (c.getHigh() != Double.NEGATIVE_INFINITY
					|| c.getLow() != Double.NEGATIVE_INFINITY);

		};
		boolean ret = false;
		for	(DurCheckPeriod c: getDurCheckPeriods())
		{
			if (check.apply(c))
			{
				ret = true;
			}
		}
		return ret;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy