diff --git a/pom.xml b/pom.xml index e511886..557f9f5 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ kotik-coder PULsE - 1.97 + 1.98 PULsE Processing Unit for Laser flash Experiments @@ -18,20 +18,19 @@ The Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt - - + org.jfree jfreechart - 1.5.0 + 1.5.4 - com.weblookandfeel - weblaf-ui - 1.2.13 + com.formdev + flatlaf + 3.0 - + org.apache.commons commons-math3 3.6.1 diff --git a/src/main/java/pulse/AbstractData.java b/src/main/java/pulse/AbstractData.java index a1d276c..58fbceb 100644 --- a/src/main/java/pulse/AbstractData.java +++ b/src/main/java/pulse/AbstractData.java @@ -258,7 +258,7 @@ public void remove(int i) { public boolean ignoreSiblings() { return true; } - + public boolean isFull() { return actualNumPoints() >= count; } @@ -306,4 +306,4 @@ public boolean equals(Object o) { } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/DiscreteInput.java b/src/main/java/pulse/DiscreteInput.java index c6bb8d8..3b29c1f 100644 --- a/src/main/java/pulse/DiscreteInput.java +++ b/src/main/java/pulse/DiscreteInput.java @@ -1,45 +1,48 @@ package pulse; import java.awt.geom.Point2D; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; import pulse.input.IndexRange; import pulse.math.Segment; -public interface DiscreteInput { - +public interface DiscreteInput extends Serializable { + public List getX(); + public List getY(); + public IndexRange getIndexRange(); - + public static List convert(double[] x, double[] y) { - + var ps = new ArrayList(); - - for(int i = 0, size = x.length; i < size; i++) { + + for (int i = 0, size = x.length; i < size; i++) { ps.add(new Point2D.Double(x[i], y[i])); } - + return ps; - + } - + public static List convert(List x, List y) { - + var ps = new ArrayList(); - - for(int i = 0, size = x.size(); i < size; i++) { + + for (int i = 0, size = x.size(); i < size; i++) { ps.add(new Point2D.Double(x.get(i), y.get(i))); } - + return ps; - + } - + public default Segment bounds() { var ir = getIndexRange(); var x = getX(); return new Segment(x.get(ir.getLowerBound()), x.get(ir.getUpperBound())); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/HeatingCurve.java b/src/main/java/pulse/HeatingCurve.java index 86e1462..f682306 100644 --- a/src/main/java/pulse/HeatingCurve.java +++ b/src/main/java/pulse/HeatingCurve.java @@ -1,6 +1,8 @@ package pulse; -import static java.util.Collections.max; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import static java.util.stream.Collectors.toList; import static pulse.input.listeners.CurveEventType.RESCALED; import static pulse.input.listeners.CurveEventType.TIME_ORIGIN_CHANGED; @@ -17,12 +19,14 @@ import org.apache.commons.math3.analysis.UnivariateFunction; import org.apache.commons.math3.analysis.interpolation.SplineInterpolator; import org.apache.commons.math3.analysis.interpolation.UnivariateInterpolator; +import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction; import pulse.baseline.Baseline; import pulse.input.ExperimentalData; import pulse.input.listeners.CurveEvent; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; +import pulse.util.FunctionSerializer; /** * The {@code HeatingCurve} represents a time-temperature profile (a @@ -42,20 +46,30 @@ */ public class HeatingCurve extends AbstractData { - private final List adjustedSignal; + /** + * + */ + private static final long serialVersionUID = 7071147065094996971L; + private List adjustedSignal; private List lastCalculation; private double startTime; - private final List listeners - = new ArrayList<>(); + private List listeners; - private UnivariateInterpolator interpolator; - private UnivariateFunction interpolation; + private transient UnivariateInterpolator interpolator; + private transient UnivariateFunction interpolation; protected HeatingCurve(List time, List signal, final double startTime, String name) { super(time, name); this.adjustedSignal = signal; this.startTime = startTime; + initListeners(); + } + + @Override + public void initListeners() { + super.initListeners(); + listeners = new ArrayList<>(); } /** @@ -167,7 +181,7 @@ public void scale(double scale) { for (int i = 0, max = Math.min(count, signal.size()); i < max; i++) { signal.set(i, signal.get(i) * scale); } - var dataEvent = new CurveEvent(RESCALED, this); + var dataEvent = new CurveEvent(RESCALED); fireCurveEvent(dataEvent); } @@ -195,14 +209,13 @@ private void refreshInterpolation() { } final double alpha = -1.0; - adjustedSignalExtended[0] = alpha * adjustedSignalExtended[2] + adjustedSignalExtended[0] = alpha * adjustedSignalExtended[2] - (1.0 - alpha) * adjustedSignalExtended[1]; // extrapolate // linearly /* * Submit to spline interpolation */ - interpolation = interpolator.interpolate(timeExtended, adjustedSignalExtended); } @@ -343,7 +356,7 @@ public NumericProperty getTimeShift() { public void setTimeShift(NumericProperty startTime) { requireType(startTime, TIME_SHIFT); this.startTime = (double) startTime.getValue(); - var dataEvent = new CurveEvent(TIME_ORIGIN_CHANGED, this); + var dataEvent = new CurveEvent(TIME_ORIGIN_CHANGED); fireCurveEvent(dataEvent); firePropertyChanged(this, startTime); } @@ -357,11 +370,14 @@ public List getBaselineCorrectedData() { } public void addHeatingCurveListener(HeatingCurveListener l) { + if (listeners == null) { + listeners = new ArrayList<>(); + } this.listeners.add(l); } @Override - public void removeHeatingCurveListeners() { + public void removeListeners() { listeners.clear(); } @@ -379,7 +395,7 @@ public boolean equals(Object o) { return super.equals(o) && adjustedSignal.containsAll(((HeatingCurve) o).adjustedSignal); } - + public double interpolateSignalAt(double x) { double min = this.timeAt(0); double max = timeLimit(); @@ -387,4 +403,23 @@ public double interpolateSignalAt(double x) { : (x < min ? signalAt(0) : signalAt(actualNumPoints() - 1)); } -} \ No newline at end of file + /* + * Serialization + */ + private void writeObject(ObjectOutputStream oos) + throws IOException { + // default serialization + oos.defaultWriteObject(); + // write the object + FunctionSerializer.writeSplineFunction((PolynomialSplineFunction) interpolation, oos); + } + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + // default deserialization + ois.defaultReadObject(); + this.interpolation = FunctionSerializer.readSplineFunction(ois); + this.interpolator = new SplineInterpolator(); + } + +} diff --git a/src/main/java/pulse/HeatingCurveListener.java b/src/main/java/pulse/HeatingCurveListener.java index 7dd972c..12d8064 100644 --- a/src/main/java/pulse/HeatingCurveListener.java +++ b/src/main/java/pulse/HeatingCurveListener.java @@ -1,15 +1,17 @@ package pulse; +import java.io.Serializable; import pulse.input.listeners.CurveEvent; /** * An interface used to listen to data events related to {@code HeatingCurve}. * */ -public interface HeatingCurveListener { +public interface HeatingCurveListener extends Serializable { /** * Signals that a {@code CurveEvent} has occurred. + * * @param event */ public void onCurveEvent(CurveEvent event); diff --git a/src/main/java/pulse/Response.java b/src/main/java/pulse/Response.java index cc4f23c..e138950 100644 --- a/src/main/java/pulse/Response.java +++ b/src/main/java/pulse/Response.java @@ -1,25 +1,27 @@ package pulse; +import java.io.Serializable; import pulse.math.Segment; import pulse.problem.schemes.solvers.SolverException; import pulse.search.GeneralTask; import pulse.search.statistics.OptimiserStatistic; -public interface Response { - +public interface Response extends Serializable { + public double evaluate(double t); + public Segment accessibleRange(); - + /** - * Calculates the value of the objective function used to identify - * the current state of the optimiser. + * Calculates the value of the objective function used to identify the + * current state of the optimiser. + * * @param task * @return the value of the objective function in the current state * @throws pulse.problem.schemes.solvers.SolverException */ - public double objectiveFunction(GeneralTask task) throws SolverException; - + public OptimiserStatistic getOptimiserStatistic(); - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/baseline/Baseline.java b/src/main/java/pulse/baseline/Baseline.java index d42c31d..29a8886 100644 --- a/src/main/java/pulse/baseline/Baseline.java +++ b/src/main/java/pulse/baseline/Baseline.java @@ -1,5 +1,6 @@ package pulse.baseline; +import java.util.ArrayList; import java.util.List; import pulse.DiscreteInput; @@ -24,7 +25,7 @@ public abstract class Baseline extends PropertyHolder implements Reflexive, Optimisable { public final static int MIN_BASELINE_POINTS = 15; - + public abstract Baseline copy(); /** @@ -60,19 +61,24 @@ public abstract class Baseline extends PropertyHolder implements Reflexive, Opti * @see fitTo(ExperimentalData,double,double) */ public void fitTo(DiscreteInput data) { - var filtered = Range.NEGATIVE.filter(data); - if(filtered[0].size() > MIN_BASELINE_POINTS) { + var filtered = Range.NEGATIVE.filter(data); + if (filtered[0].size() > MIN_BASELINE_POINTS) { doFit(filtered[0], filtered[1]); - } + } } - + public void fitTo(List x, List y) { - int index = IndexRange.closestLeft(0, x); - var xx = x.subList(0, index + 1); - var yy = y.subList(0, index + 1); - if(xx.size() > MIN_BASELINE_POINTS) { + int index = IndexRange.closestLeft(0, x); + var xx = new ArrayList<>(x.subList(0, index + 1)); + var yy = new ArrayList<>(y.subList(0, index + 1)); + if (xx.size() > MIN_BASELINE_POINTS) { doFit(xx, yy); - } + } } - -} \ No newline at end of file + + @Override + public String getDescriptor() { + return "Baseline"; + } + +} diff --git a/src/main/java/pulse/baseline/FlatBaseline.java b/src/main/java/pulse/baseline/FlatBaseline.java index 9500c8f..1ca0743 100644 --- a/src/main/java/pulse/baseline/FlatBaseline.java +++ b/src/main/java/pulse/baseline/FlatBaseline.java @@ -8,10 +8,11 @@ /** * A flat baseline. */ - public class FlatBaseline extends AdjustableBaseline { - - /** + + private static final long serialVersionUID = -4867631788950622739L; + + /** * A primitive constructor, which initialises a {@code CONSTANT} baseline * with zero intercept and slope. */ @@ -27,8 +28,7 @@ public FlatBaseline() { public FlatBaseline(double intercept) { super(intercept, 0.0); } - - + @Override protected void doFit(List x, List y) { double intercept = mean(y); @@ -37,12 +37,12 @@ protected void doFit(List x, List y) { @Override public Baseline copy() { - return new FlatBaseline((double)getIntercept().getValue()); + return new FlatBaseline((double) getIntercept().getValue()); } @Override public String toString() { return getClass().getSimpleName() + " = " + format("%3.2f", getIntercept().getValue()); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/baseline/LinearBaseline.java b/src/main/java/pulse/baseline/LinearBaseline.java index d3d899e..a04354d 100644 --- a/src/main/java/pulse/baseline/LinearBaseline.java +++ b/src/main/java/pulse/baseline/LinearBaseline.java @@ -29,6 +29,8 @@ */ public class LinearBaseline extends AdjustableBaseline { + private static final long serialVersionUID = -7334390731462268504L; + /** * A primitive constructor, which initialises a {@code CONSTANT} baseline * with zero intercept and slope. @@ -36,19 +38,19 @@ public class LinearBaseline extends AdjustableBaseline { public LinearBaseline() { super(0.0, 0.0); } - + public LinearBaseline(double intercept, double slope) { super(intercept, slope); } - - public LinearBaseline(LinearBaseline baseline) { - super( (double) baseline.getIntercept().getValue(), - (double) baseline.getSlope().getValue() - ); + + public LinearBaseline(AdjustableBaseline baseline) { + super((double) baseline.getIntercept().getValue(), + (double) baseline.getSlope().getValue() + ); } @Override - protected void doFit(List x, List y) { + protected void doFit(List x, List y) { double meanx = mean(x); double meany = mean(y); @@ -63,7 +65,7 @@ protected void doFit(List x, List y) { xxbar += (x1 - meanx) * (x1 - meanx); xybar += (x1 - meanx) * (y1 - meany); } - + double slope = xybar / xxbar; double intercept = meany - slope * meanx; @@ -74,8 +76,8 @@ protected void doFit(List x, List y) { @Override public String toString() { var slope = getSlope().getValue(); - return getClass().getSimpleName() + " = " + - format("%3.2f + t * ( %3.2f )", getIntercept().getValue(), slope); + return getClass().getSimpleName() + " = " + + format("%3.2f + t * ( %3.2f )", getIntercept().getValue(), slope); } @Override @@ -121,9 +123,9 @@ public void assign(ParameterVector params) { for (Parameter p : params.getParameters()) { var key = p.getIdentifier().getKeyword(); - + if (key == BASELINE_SLOPE) { - setSlope( derive(BASELINE_SLOPE, p.inverseTransform() )); + setSlope(derive(BASELINE_SLOPE, p.inverseTransform())); } } diff --git a/src/main/java/pulse/baseline/SinusoidalBaseline.java b/src/main/java/pulse/baseline/SinusoidalBaseline.java index 9074cdc..94b92c9 100644 --- a/src/main/java/pulse/baseline/SinusoidalBaseline.java +++ b/src/main/java/pulse/baseline/SinusoidalBaseline.java @@ -46,6 +46,7 @@ */ public class SinusoidalBaseline extends LinearBaseline { + private static final long serialVersionUID = -6858521208790195992L; private List hiFreq; private List loFreq; private List active; @@ -87,10 +88,10 @@ public Baseline copy() { newH.setParent(baseline); } for (Harmonic h : hiFreq) { - baseline.hiFreq.add(new Harmonic(h)); + baseline.hiFreq.add(new Harmonic(h)); } for (Harmonic h : loFreq) { - baseline.loFreq.add(new Harmonic(h)); + baseline.loFreq.add(new Harmonic(h)); } return baseline; } @@ -98,7 +99,7 @@ public Baseline copy() { @Override public void optimisationVector(ParameterVector output) { super.optimisationVector(output); - active.forEach(h -> h.optimisationVector(output) ); + active.forEach(h -> h.optimisationVector(output)); } @Override @@ -124,7 +125,7 @@ private void guessHarmonics(double[] x, double[] y) { double maxAmp = 0; hiFreq = new ArrayList<>(); - + double span = x[x.length - 1] - x[0]; double lowerFrequency = 4.0 / span; @@ -146,7 +147,7 @@ private List sort(List hs, int limit) { tmp.sort(null); Collections.reverse(tmp); //leave out a maximum of n harmonics - return tmp.subList(0, Math.min(tmp.size(), limit)); + return new ArrayList<>(tmp.subList(0, Math.min(tmp.size(), limit))); } private void labelActive() { @@ -237,25 +238,25 @@ public NumericProperty getHiFreqMax() { public void setHiFreqMax(NumericProperty maxHarmonics) { NumericProperty.requireType(maxHarmonics, MAX_HIGH_FREQ_WAVES); int oldValue = this.maxHighFreqHarmonics; - + if ((int) maxHarmonics.getValue() != oldValue) { - + var lowFreq = new ArrayList(); int size = active.size(); - - if(maxHighFreqHarmonics < size) { + + if (maxHighFreqHarmonics < size) { lowFreq = new ArrayList<>(active.subList(maxHighFreqHarmonics, size)); } - + this.maxHighFreqHarmonics = (int) maxHarmonics.getValue(); active.clear(); active.addAll(sort(hiFreq, maxHighFreqHarmonics)); active.addAll(lowFreq); this.labelActive(); this.firePropertyChanged(this, maxHarmonics); - + } - + } public NumericProperty getLowFreqMax() { @@ -267,7 +268,7 @@ public void setLowFreqMax(NumericProperty maxHarmonics) { int oldValue = this.maxLowFreqHarmonics; if ((int) maxHarmonics.getValue() != oldValue) { this.maxLowFreqHarmonics = (int) maxHarmonics.getValue(); - active = active.subList(0, maxHighFreqHarmonics); + active = new ArrayList<>(active.subList(0, maxHighFreqHarmonics)); active.addAll(this.sort(loFreq, maxLowFreqHarmonics)); this.labelActive(); this.firePropertyChanged(this, maxHarmonics); @@ -279,7 +280,7 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { super.set(type, property); switch (type) { - + case MAX_HIGH_FREQ_WAVES: setHiFreqMax(property); break; @@ -346,12 +347,11 @@ private void addLowFreq(DiscreteInput input) { /* These harmonics are inaccessible by FFT */ - for (double f = freq; f > 1.0 / (2.0 * span); f /= 2.0) { loFreq.add(new Harmonic(amp, f, 0.0)); } - active.addAll(loFreq.subList(0, Math.min(loFreq.size(), maxLowFreqHarmonics))); + active.addAll(loFreq.subList(0, Math.min(loFreq.size(), maxLowFreqHarmonics))); } @Override diff --git a/src/main/java/pulse/input/ExperimentalData.java b/src/main/java/pulse/input/ExperimentalData.java index f38a8e0..4be76e7 100644 --- a/src/main/java/pulse/input/ExperimentalData.java +++ b/src/main/java/pulse/input/ExperimentalData.java @@ -27,11 +27,15 @@ */ public class ExperimentalData extends AbstractData implements DiscreteInput { + /** + * + */ + private static final long serialVersionUID = 7950893319753173094L; private HalfTimeCalculator calculator; private Metadata metadata; private IndexRange indexRange; private Range range; - private List dataListeners; + private transient List dataListeners; /** * This is the cutoff factor which is used as a criterion for data @@ -49,10 +53,16 @@ public class ExperimentalData extends AbstractData implements DiscreteInput { */ public ExperimentalData() { super(); - dataListeners = new ArrayList<>(); setPrefix("RawData"); setNumPoints(derive(NUMPOINTS, 0)); - indexRange = new IndexRange(0,0); + indexRange = new IndexRange(0, 0); + initListeners(); + } + + @Override + public void initListeners() { + super.initListeners(); + dataListeners = new ArrayList<>(); this.addDataListener((DataEvent e) -> { if (e.getType() == DataEventType.DATA_LOADED) { preprocess(); @@ -61,6 +71,9 @@ public ExperimentalData() { } public final void addDataListener(DataListener listener) { + if(dataListeners == null) { + dataListeners = new ArrayList<>(); + } dataListeners.add(listener); } @@ -298,4 +311,4 @@ public List getY() { return this.getSignalData(); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/input/IndexRange.java b/src/main/java/pulse/input/IndexRange.java index 93a9280..cb19490 100644 --- a/src/main/java/pulse/input/IndexRange.java +++ b/src/main/java/pulse/input/IndexRange.java @@ -1,5 +1,6 @@ package pulse.input; +import java.io.Serializable; import static java.util.Objects.requireNonNull; import java.util.List; @@ -15,8 +16,9 @@ * @see pulse.input.Range * */ -public class IndexRange { +public class IndexRange implements Serializable { + private static final long serialVersionUID = 7983756487957427969L; private int iStart; private int iEnd; @@ -24,7 +26,7 @@ public IndexRange(IndexRange other) { iStart = other.iStart; iEnd = other.iEnd; } - + public IndexRange(int start, int end) { this.iStart = start; this.iEnd = end; diff --git a/src/main/java/pulse/input/InterpolationDataset.java b/src/main/java/pulse/input/InterpolationDataset.java index 45f244e..4e0ec64 100644 --- a/src/main/java/pulse/input/InterpolationDataset.java +++ b/src/main/java/pulse/input/InterpolationDataset.java @@ -1,20 +1,18 @@ package pulse.input; -import static pulse.properties.NumericPropertyKeyword.CONDUCTIVITY; -import static pulse.properties.NumericPropertyKeyword.DENSITY; -import static pulse.properties.NumericPropertyKeyword.SPECIFIC_HEAT; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import java.util.ArrayList; -import java.util.EnumMap; import java.util.List; -import java.util.Map; import org.apache.commons.math3.analysis.UnivariateFunction; import org.apache.commons.math3.analysis.interpolation.AkimaSplineInterpolator; +import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction; -import pulse.input.listeners.ExternalDatasetListener; -import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.EMISSIVITY; +import pulse.util.FunctionSerializer; import pulse.util.ImmutableDataEntry; /** @@ -27,19 +25,19 @@ * * @see pulse.input.listeners.ExternalDatasetListener */ -public class InterpolationDataset { +public class InterpolationDataset implements Serializable { - private UnivariateFunction interpolation; + /** + * + */ + private static final long serialVersionUID = 7439474910490135034L; + private transient UnivariateFunction interpolation; private final List> dataset; - private static final Map standartDatasets - = new EnumMap(StandartType.class); - private static final List listeners = new ArrayList<>(); - /** - * Creates an empty {@code InterpolationDataset}. - */ - - public InterpolationDataset() { + /** + * Creates an empty {@code InterpolationDataset}. + */ + public InterpolationDataset() { dataset = new ArrayList<>(); } @@ -66,7 +64,7 @@ public void add(ImmutableDataEntry entry) { } /** - * Constructs a new spline interpolator and uses the available dataset to + * Constructs a new Akima spline interpolator and uses the available dataset to * produce a {@code SplineInterpolation}. */ public void doInterpolation() { @@ -84,65 +82,22 @@ public List> getData() { return dataset; } - /** - * Retrieves a standard dataset previously loaded by the respective reader. - * - * @param type the standard dataset type - * @return an {@code InterpolationDataset} corresponding to {@code type} - */ - public static InterpolationDataset getDataset(StandartType type) { - return standartDatasets.get(type); - } - - /** - * Puts a datset specified by {@code type} into the static hash map of this - * class, using {@code type} as key. Triggers {@code onDensityDataLoaded} - * - * @param dataset a dataset to be appended to the static hash map - * @param type the dataset type + /* + * Serialization */ - public static void setDataset(InterpolationDataset dataset, StandartType type) { - standartDatasets.put(type, dataset); - listeners.stream().forEach(l -> l.onDataLoaded(type)); + private void writeObject(ObjectOutputStream oos) + throws IOException { + // default serialization + oos.defaultWriteObject(); + // write the object + FunctionSerializer.writeSplineFunction((PolynomialSplineFunction) interpolation, oos); } - /** - * Creates a list of property keywords that can be derived with help of the - * loaded data. For example, if heat capacity and density data is available, - * the returned list will contain {@code CONDUCTIVITY}. - * - * @return - */ - public static List derivableProperties() { - var list = new ArrayList(); - if (standartDatasets.containsKey(StandartType.HEAT_CAPACITY)) { - list.add(SPECIFIC_HEAT); - } - if (standartDatasets.containsKey(StandartType.DENSITY)) { - list.add(DENSITY); - } - if (list.contains(SPECIFIC_HEAT) && list.contains(DENSITY)) { - list.add(CONDUCTIVITY); - list.add(EMISSIVITY); - } - return list; - } - - public static void addListener(ExternalDatasetListener l) { - listeners.add(l); - } - - public enum StandartType { - - /** - * A keyword for the heat capacity dataset (in J/kg/K). - */ - HEAT_CAPACITY, - /** - * A keyword for the density dataset (in kg/m3). - */ - DENSITY; - + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + // default deserialization + ois.defaultReadObject(); + this.interpolation = FunctionSerializer.readSplineFunction(ois); } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/input/Metadata.java b/src/main/java/pulse/input/Metadata.java index cd9369c..6840ff5 100644 --- a/src/main/java/pulse/input/Metadata.java +++ b/src/main/java/pulse/input/Metadata.java @@ -42,11 +42,12 @@ */ public class Metadata extends PropertyHolder implements Reflexive { + private static final long serialVersionUID = -7954252611294551707L; private Set data; private SampleName sampleName; private int externalID; - private InstanceDescriptor pulseDescriptor + private InstanceDescriptor pulseDescriptor = new InstanceDescriptor<>("Pulse Shape Selector", PulseTemporalShape.class); private NumericPulseData pulseData; @@ -61,7 +62,7 @@ public class Metadata extends PropertyHolder implements Reflexive { * experimental setup. */ public Metadata(NumericProperty temperature, int externalId) { - sampleName = new SampleName(); + sampleName = new SampleName(null); setExternalID(externalId); pulseDescriptor.setSelectedDescriptor(RectangularPulse.class.getSimpleName()); data = new TreeSet<>(); @@ -117,13 +118,14 @@ public void setSampleName(SampleName sampleName) { public final void setPulseData(NumericPulseData pulseData) { this.pulseData = pulseData; - this.set(PULSE_WIDTH, derive(PULSE_WIDTH, pulseData.pulseWidth()) ); + this.set(PULSE_WIDTH, derive(PULSE_WIDTH, pulseData.pulseWidth())); } /** * If a Numerical Pulse has been loaded (for example, when importing from * Proteus), this will return an object describing this data. - * @return + * + * @return */ public final NumericPulseData getPulseData() { return pulseData; @@ -186,7 +188,7 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { @Override public List listedTypes() { List list = super.listedTypes(); - list.add(new SampleName()); + list.add(new SampleName("")); list.add(pulseDescriptor); return list; } @@ -272,4 +274,4 @@ public boolean equals(Object o) { } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/input/Range.java b/src/main/java/pulse/input/Range.java index fd6e4e9..09b247d 100644 --- a/src/main/java/pulse/input/Range.java +++ b/src/main/java/pulse/input/Range.java @@ -18,7 +18,6 @@ import pulse.math.Segment; import pulse.math.transforms.StickTransform; import pulse.problem.schemes.solvers.SolverException; -import pulse.properties.Flag; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; import pulse.search.Optimisable; @@ -32,12 +31,14 @@ */ public class Range extends PropertyHolder implements Optimisable { - private Segment segment; - - public final static Range UNLIMITED = new Range (Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + private static final long serialVersionUID = 5326569416384623525L; + + private final Segment segment; + + public final static Range UNLIMITED = new Range(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); public final static Range NEGATIVE = new Range(Double.NEGATIVE_INFINITY, -1E-16); public final static Range POSITIVE = new Range(1e-16, Double.POSITIVE_INFINITY); - + /** * Constructs a {@code Range} from the minimum and maximum values of * {@code data}. @@ -60,44 +61,43 @@ public Range(List data) { public Range(double a, double b) { this.segment = new Segment(a, b); } - + /** - * Contains a data double array ([0] - x, [1] - y), - * where the data points have been filtered so that - * each x fits into this range. + * Contains a data double array ([0] - x, [1] - y), where the data points + * have been filtered so that each x fits into this range. + * * @param input * @return a [2][...] array containing filtered x and y values */ - public List[] filter(DiscreteInput input) { - var x = input.getX(); - var y = input.getY(); - - if(x.size() != y.size()) { - throw new IllegalArgumentException("x.length != y.length"); - } - - var xf = new ArrayList(); - var yf = new ArrayList(); - - double min = segment.getMinimum(); - double max = segment.getMaximum(); - - final double eps = 1E-10; - - for(int i = 0, size = x.size(); i < size; i++) { - - if(x.get(i) > min && x.get(i) < max + eps) { - - xf.add(x.get(i)); - yf.add(y.get(i)); - - } - - } - - return new List[]{xf, yf}; - + var x = input.getX(); + var y = input.getY(); + + if (x.size() != y.size()) { + throw new IllegalArgumentException("x.length != y.length"); + } + + var xf = new ArrayList(); + var yf = new ArrayList(); + + double min = segment.getMinimum(); + double max = segment.getMaximum(); + + final double eps = 1E-10; + + for (int i = 0, size = x.size(); i < size; i++) { + + if (x.get(i) > min && x.get(i) < max + eps) { + + xf.add(x.get(i)); + yf.add(y.get(i)); + + } + + } + + return new List[]{xf, yf}; + } /** @@ -139,12 +139,12 @@ public NumericProperty getUpperBound() { */ public void setLowerBound(NumericProperty p) { requireType(p, LOWER_BOUND); - - if( boundLimits(false).contains( ((Number)p.getValue()).doubleValue())) { + + if (boundLimits(false).contains(((Number) p.getValue()).doubleValue())) { segment.setMinimum((double) p.getValue()); firePropertyChanged(this, p); } - + } /** @@ -154,12 +154,12 @@ public void setLowerBound(NumericProperty p) { */ public void setUpperBound(NumericProperty p) { requireType(p, UPPER_BOUND); - - if( boundLimits(true).contains( ((Number)p.getValue()).doubleValue()) ) { + + if (boundLimits(true).contains(((Number) p.getValue()).doubleValue())) { segment.setMaximum((double) p.getValue()); firePropertyChanged(this, p); } - + } /** @@ -215,26 +215,28 @@ public Set listedKeywords() { set.add(LOWER_BOUND); set.add(UPPER_BOUND); return set; - } - + } + /** * Calculates the allowed range for either the upper or lower bound. - * @param isUpperBound if {@code true}, will calculate the range for the upper bound, otherwise -- for the lower one., + * + * @param isUpperBound if {@code true}, will calculate the range for the + * upper bound, otherwise -- for the lower one., * @return a Segment range of limits for the specific bound */ - public Segment boundLimits(boolean isUpperBound) { - + var curve = (ExperimentalData) this.getParent(); var seq = curve.getTimeSequence(); double tHalf = curve.getHalfTimeCalculator().getHalfTime(); - + Segment result = null; - if(isUpperBound) + if (isUpperBound) { result = new Segment(2.5 * tHalf, seq.get(seq.size() - 1)); - else - result = new Segment( Math.max(-0.15 * tHalf, seq.get(0)), 0.75 * tHalf); - + } else { + result = new Segment(Math.max(-0.15 * tHalf, seq.get(0)), 0.75 * tHalf); + } + return result; } @@ -250,14 +252,14 @@ public Segment boundLimits(boolean isUpperBound) { */ @Override public void optimisationVector(ParameterVector output) { - + Segment bounds; - + for (Parameter p : output.getParameters()) { var key = p.getIdentifier().getKeyword(); double value; - + switch (key) { case UPPER_BOUND: bounds = boundLimits(true); @@ -270,7 +272,7 @@ public void optimisationVector(ParameterVector output) { default: continue; } - + var transform = new StickTransform(bounds); p.setBounds(bounds); @@ -292,7 +294,7 @@ public void assign(ParameterVector params) throws SolverException { for (Parameter p : params.getParameters()) { var key = p.getIdentifier().getKeyword(); - var np = derive( key, p.inverseTransform() ); + var np = derive(key, p.inverseTransform()); switch (key) { case UPPER_BOUND: @@ -313,4 +315,4 @@ public String toString() { return "Range given by: " + segment.toString(); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/input/listeners/CurveEvent.java b/src/main/java/pulse/input/listeners/CurveEvent.java index a0b69f9..61413bf 100644 --- a/src/main/java/pulse/input/listeners/CurveEvent.java +++ b/src/main/java/pulse/input/listeners/CurveEvent.java @@ -1,6 +1,6 @@ package pulse.input.listeners; -import pulse.HeatingCurve; +import java.io.Serializable; /** * A {@code CurveEvent} is associated with an {@code HeatingCurve} object. @@ -8,21 +8,18 @@ * @see pulse.HeatingCurve * */ -public class CurveEvent { +public class CurveEvent implements Serializable { private CurveEventType type; - private HeatingCurve data; /** * Constructs a {@code CurveEvent} object, combining the {@code type} and * associated {@code data} * * @param type the type of this event - * @param data the source of the event */ - public CurveEvent(CurveEventType type, HeatingCurve data) { + public CurveEvent(CurveEventType type) { this.type = type; - this.data = data; } /** @@ -34,14 +31,4 @@ public CurveEventType getType() { return type; } - /** - * Used to get the {@code HeatingCurve} object that has undergone certain - * changes specified by this event type. - * - * @return the associated data - */ - public HeatingCurve getData() { - return data; - } - } diff --git a/src/main/java/pulse/input/listeners/CurveEventType.java b/src/main/java/pulse/input/listeners/CurveEventType.java index 3dcb5d0..40a55d3 100644 --- a/src/main/java/pulse/input/listeners/CurveEventType.java +++ b/src/main/java/pulse/input/listeners/CurveEventType.java @@ -19,12 +19,10 @@ public enum CurveEventType { * procedure. */ TIME_ORIGIN_CHANGED, - /** - * A calculation associated with this curve has finished and - * the required arrays have been filled. + * A calculation associated with this curve has finished and the required + * arrays have been filled. */ - CALCULATION_FINISHED; } diff --git a/src/main/java/pulse/input/listeners/DataEvent.java b/src/main/java/pulse/input/listeners/DataEvent.java index d42c1a2..7fefe1c 100644 --- a/src/main/java/pulse/input/listeners/DataEvent.java +++ b/src/main/java/pulse/input/listeners/DataEvent.java @@ -1,5 +1,6 @@ package pulse.input.listeners; +import java.io.Serializable; import pulse.AbstractData; /** @@ -7,7 +8,7 @@ * {@code ExperimentalData}. * */ -public class DataEvent { +public class DataEvent implements Serializable { private DataEventType type; private AbstractData data; diff --git a/src/main/java/pulse/input/listeners/DataEventType.java b/src/main/java/pulse/input/listeners/DataEventType.java index da2160c..52c8085 100644 --- a/src/main/java/pulse/input/listeners/DataEventType.java +++ b/src/main/java/pulse/input/listeners/DataEventType.java @@ -15,11 +15,9 @@ public enum DataEventType { */ RANGE_CHANGED, - /** * All data points loaded and are ready for processing. */ - DATA_LOADED; } diff --git a/src/main/java/pulse/input/listeners/DataListener.java b/src/main/java/pulse/input/listeners/DataListener.java index cb1f3d4..28170c5 100644 --- a/src/main/java/pulse/input/listeners/DataListener.java +++ b/src/main/java/pulse/input/listeners/DataListener.java @@ -1,11 +1,13 @@ package pulse.input.listeners; +import java.io.Serializable; + /** * A listener interface, which is used to listen to {@code DataEvent}s occurring * with an {@code ExperimentalData} object. * */ -public interface DataListener { +public interface DataListener extends Serializable { /** * Triggered when a certain {@code DataEvent} specified by its diff --git a/src/main/java/pulse/input/listeners/ExternalDatasetListener.java b/src/main/java/pulse/input/listeners/ExternalDatasetListener.java index 1bb8539..a73d3d1 100644 --- a/src/main/java/pulse/input/listeners/ExternalDatasetListener.java +++ b/src/main/java/pulse/input/listeners/ExternalDatasetListener.java @@ -1,7 +1,6 @@ package pulse.input.listeners; -import pulse.input.InterpolationDataset.StandartType; - +import java.io.Serializable; /** * A listener associated with the {@code InterpolationDataset} static repository * of interpolations. @@ -9,11 +8,7 @@ */ public interface ExternalDatasetListener { - /** - * Triggered when a data {@code type} has been loaded. - * - * @param type a type of the dataset, for which an interpolation is created. - */ - public void onDataLoaded(StandartType type); + public void onSpecificHeatDataLoaded(); + public void onDensityDataLoaded(); -} +} \ No newline at end of file diff --git a/src/main/java/pulse/io/export/ExportManager.java b/src/main/java/pulse/io/export/ExportManager.java index 2064c50..892402c 100644 --- a/src/main/java/pulse/io/export/ExportManager.java +++ b/src/main/java/pulse/io/export/ExportManager.java @@ -21,7 +21,7 @@ * */ public class ExportManager { - + //current working dir private static File cwd = null; diff --git a/src/main/java/pulse/io/export/LogPaneExporter.java b/src/main/java/pulse/io/export/TextLogPaneExporter.java similarity index 67% rename from src/main/java/pulse/io/export/LogPaneExporter.java rename to src/main/java/pulse/io/export/TextLogPaneExporter.java index ade0e0f..317af50 100644 --- a/src/main/java/pulse/io/export/LogPaneExporter.java +++ b/src/main/java/pulse/io/export/TextLogPaneExporter.java @@ -4,22 +4,23 @@ import java.io.FileOutputStream; import java.io.IOException; +import javax.swing.JEditorPane; import javax.swing.text.BadLocationException; import javax.swing.text.html.HTMLEditorKit; -import pulse.ui.components.LogPane; +import pulse.ui.components.TextLogPane; /** * Similar to a {@code LogExporter}, except that it works only on the contents * of a {@code LogPane} currently being displayed to the user. * */ -public class LogPaneExporter implements Exporter { +public class TextLogPaneExporter implements Exporter { - private static LogPaneExporter instance = new LogPaneExporter(); + private static TextLogPaneExporter instance = new TextLogPaneExporter(); - private LogPaneExporter() { + private TextLogPaneExporter() { // intentionally blank } @@ -29,10 +30,11 @@ private LogPaneExporter() { * argument is ignored. After exporting, the stream is explicitly closed. */ @Override - public void printToStream(LogPane pane, FileOutputStream fos, Extension extension) { - var kit = (HTMLEditorKit) pane.getEditorKit(); + public void printToStream(TextLogPane pane, FileOutputStream fos, Extension extension) { + var editorPane = (JEditorPane) pane.getGUIComponent(); + var kit = (HTMLEditorKit) editorPane.getEditorKit(); try { - kit.write(fos, pane.getDocument(), 0, pane.getDocument().getLength()); + kit.write(fos, editorPane.getDocument(), 0, editorPane.getDocument().getLength()); } catch (IOException | BadLocationException e) { System.err.println("Could not export the log pane!"); e.printStackTrace(); @@ -50,7 +52,7 @@ public void printToStream(LogPane pane, FileOutputStream fos, Extension extensio * * @return an instance of{@code LogPaneExporter}. */ - public static LogPaneExporter getInstance() { + public static TextLogPaneExporter getInstance() { return instance; } @@ -58,8 +60,8 @@ public static LogPaneExporter getInstance() { * @return {@code LogPane.class}. */ @Override - public Class target() { - return LogPane.class; + public Class target() { + return TextLogPane.class; } /** diff --git a/src/main/java/pulse/io/readers/AbstractReader.java b/src/main/java/pulse/io/readers/AbstractReader.java index d98036e..29bbc62 100644 --- a/src/main/java/pulse/io/readers/AbstractReader.java +++ b/src/main/java/pulse/io/readers/AbstractReader.java @@ -13,6 +13,7 @@ * lists, arrays and containers may (and usually will) change as a result of * using the reader. *

+ * * @param */ public interface AbstractReader extends AbstractHandler { diff --git a/src/main/java/pulse/io/readers/NetzschCSVReader.java b/src/main/java/pulse/io/readers/NetzschCSVReader.java index 85c4f1c..d9837f1 100644 --- a/src/main/java/pulse/io/readers/NetzschCSVReader.java +++ b/src/main/java/pulse/io/readers/NetzschCSVReader.java @@ -23,6 +23,7 @@ import pulse.input.Metadata; import pulse.input.Range; import pulse.properties.NumericPropertyKeyword; +import pulse.properties.SampleName; import pulse.ui.Messages; /** @@ -49,6 +50,7 @@ public class NetzschCSVReader implements CurveReader { private final static String DIAMETER = "Diameter"; private final static String L_PULSE_WIDTH = "Laser_pulse_width"; private final static String PULSE_WIDTH = "Pulse_width"; + private final static String MATERIAL = "Material"; /** * Note comma is included as a delimiter character here. @@ -115,6 +117,9 @@ public List read(File file) throws IOException { int shotId = determineShotID(reader, file); + String name = findLineByLabel(reader, MATERIAL, DETECTOR_SPOT_SIZE, true) + .substring(MATERIAL.length() + 1) + .replaceAll(delims, ""); String spot = findLineByLabel(reader, DETECTOR_SPOT_SIZE, THICKNESS, false); double spotSize = 0; @@ -164,6 +169,7 @@ public List read(File file) throws IOException { if (pulseWidth > 1e-10) { met.set(NumericPropertyKeyword.PULSE_WIDTH, derive(NumericPropertyKeyword.PULSE_WIDTH, pulseWidth)); } + met.setSampleName(new SampleName(name)); met.set(NumericPropertyKeyword.THICKNESS, derive(NumericPropertyKeyword.THICKNESS, thickness)); met.set(NumericPropertyKeyword.DIAMETER, derive(NumericPropertyKeyword.DIAMETER, diameter)); met.set(NumericPropertyKeyword.FOV_OUTER, derive(NumericPropertyKeyword.FOV_OUTER, spotSize != 0 ? spotSize : 0.85 * diameter)); diff --git a/src/main/java/pulse/io/readers/NetzschPulseCSVReader.java b/src/main/java/pulse/io/readers/NetzschPulseCSVReader.java index 7d759e6..bfc07fe 100644 --- a/src/main/java/pulse/io/readers/NetzschPulseCSVReader.java +++ b/src/main/java/pulse/io/readers/NetzschPulseCSVReader.java @@ -55,8 +55,8 @@ public NumericPulseData read(File file) throws IOException { Objects.requireNonNull(file, Messages.getString("DATReader.1")); NumericPulseData data = null; - - ( (NetzschCSVReader) NetzschCSVReader.getInstance() ) + + ((NetzschCSVReader) NetzschCSVReader.getInstance()) .setDefaultLocale(); //always start with a default locale try (BufferedReader reader = new BufferedReader(new FileReader(file))) { diff --git a/src/main/java/pulse/io/readers/ReaderManager.java b/src/main/java/pulse/io/readers/ReaderManager.java index 0f8201b..5d168a9 100644 --- a/src/main/java/pulse/io/readers/ReaderManager.java +++ b/src/main/java/pulse/io/readers/ReaderManager.java @@ -214,27 +214,28 @@ public static Set readDirectory(List> readers, File dir } var es = Executors.newSingleThreadExecutor(); - + var callableList = new ArrayList>(); - + for (File f : directory.listFiles()) { Callable callable = () -> read(readers, f); callableList.add(callable); } - + Set result = new HashSet<>(); - + try { List> futures = es.invokeAll(callableList); - - for(Future f : futures) + + for (Future f : futures) { result.add(f.get()); - + } + } catch (InterruptedException ex) { - Logger.getLogger(ReaderManager.class.getName()).log(Level.SEVERE, + Logger.getLogger(ReaderManager.class.getName()).log(Level.SEVERE, "Reading interrupted when loading files from " + directory.toString(), ex); } catch (ExecutionException ex) { - Logger.getLogger(ReaderManager.class.getName()).log(Level.SEVERE, + Logger.getLogger(ReaderManager.class.getName()).log(Level.SEVERE, "Error executing read operation using concurrency", ex); } diff --git a/src/main/java/pulse/math/AbstractIntegrator.java b/src/main/java/pulse/math/AbstractIntegrator.java index 1fadb3a..b56c00e 100644 --- a/src/main/java/pulse/math/AbstractIntegrator.java +++ b/src/main/java/pulse/math/AbstractIntegrator.java @@ -1,5 +1,6 @@ package pulse.math; +import java.io.Serializable; import pulse.util.PropertyHolder; import pulse.util.Reflexive; @@ -10,7 +11,7 @@ * or more variables and the other to actually do the integration. * */ -public abstract class AbstractIntegrator extends PropertyHolder implements Reflexive { +public abstract class AbstractIntegrator extends PropertyHolder implements Reflexive, Serializable { private Segment integrationBounds; diff --git a/src/main/java/pulse/math/FFTTransformer.java b/src/main/java/pulse/math/FFTTransformer.java index 7e5390d..dea0d65 100644 --- a/src/main/java/pulse/math/FFTTransformer.java +++ b/src/main/java/pulse/math/FFTTransformer.java @@ -1,15 +1,17 @@ package pulse.math; +import java.io.Serializable; import org.apache.commons.math3.complex.Complex; import org.apache.commons.math3.transform.DftNormalization; import org.apache.commons.math3.transform.FastFourierTransformer; import org.apache.commons.math3.transform.TransformType; -public class FFTTransformer { +public class FFTTransformer implements Serializable { + private static final long serialVersionUID = -5424502578926616928L; private double[] amplitudeSpec; private double[] phaseSpec; - + private int n; //number of input points private Complex[] buffer; @@ -18,7 +20,7 @@ public class FFTTransformer { public FFTTransformer(double[] realInput) { this(Window.HANN, realInput, new double[realInput.length]); } - + public FFTTransformer(Window window, double[] realInput) { this(window, realInput, new double[realInput.length]); } @@ -68,7 +70,7 @@ public FFTTransformer(Window window, double[] realInput, double[] imagInput) { public double[] sampling(double[] x) { final double totalTime = x[n - 2] - x[0]; double[] sample = new double[buffer.length / 2]; - double fs = n/totalTime; //sampling rate + double fs = n / totalTime; //sampling rate for (int i = 0; i < sample.length; i++) { sample[i] = i * fs / buffer.length; } @@ -132,5 +134,5 @@ public double[] getAmpltiudeSpectrum() { public double[] getPhaseSpectrum() { return phaseSpec; } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/math/FixedIntervalIntegrator.java b/src/main/java/pulse/math/FixedIntervalIntegrator.java index ac77ccb..8631efb 100644 --- a/src/main/java/pulse/math/FixedIntervalIntegrator.java +++ b/src/main/java/pulse/math/FixedIntervalIntegrator.java @@ -20,6 +20,7 @@ */ public abstract class FixedIntervalIntegrator extends AbstractIntegrator { + private static final long serialVersionUID = -5304597610450009326L; private int integrationSegments; /** diff --git a/src/main/java/pulse/math/FunctionWithInterpolation.java b/src/main/java/pulse/math/FunctionWithInterpolation.java index 9cbf3d4..41971b7 100644 --- a/src/main/java/pulse/math/FunctionWithInterpolation.java +++ b/src/main/java/pulse/math/FunctionWithInterpolation.java @@ -1,18 +1,25 @@ package pulse.math; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import org.apache.commons.math3.analysis.UnivariateFunction; import org.apache.commons.math3.analysis.interpolation.SplineInterpolator; +import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction; +import pulse.util.FunctionSerializer; /** * An abstract class for univariate functions with the capacity of spline * interpolation. * */ -public abstract class FunctionWithInterpolation { +public abstract class FunctionWithInterpolation implements Serializable { + private static final long serialVersionUID = -303222542756574714L; private Segment tBounds; private int lookupTableSize; - private UnivariateFunction interpolation; + private transient UnivariateFunction interpolation; public final static int NUM_PARTITIONS = 8192; @@ -114,4 +121,26 @@ private void interpolate(double[] lookupTable) { interpolation = splineInterpolation.interpolate(tArray, lookupTable); } + /* + * Serialization + */ + private void writeObject(ObjectOutputStream oos) + throws IOException { + // default serialization + oos.defaultWriteObject(); + // write the object + oos.writeObject(tBounds); + oos.writeInt(lookupTableSize); + FunctionSerializer.writeSplineFunction((PolynomialSplineFunction) interpolation, oos); + } + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + // default deserialization + ois.defaultReadObject(); + this.tBounds = (Segment) ois.readObject(); + this.lookupTableSize = ois.readInt(); + this.interpolation = FunctionSerializer.readSplineFunction(ois); + } + } diff --git a/src/main/java/pulse/math/Harmonic.java b/src/main/java/pulse/math/Harmonic.java index 06e3604..b5207f3 100644 --- a/src/main/java/pulse/math/Harmonic.java +++ b/src/main/java/pulse/math/Harmonic.java @@ -30,6 +30,8 @@ */ public class Harmonic extends PropertyHolder implements Optimisable, Comparable { + private static final long serialVersionUID = 3732379391172485157L; + private int rank = -1; private double amplitude; @@ -135,7 +137,7 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { public void optimisationVector(ParameterVector output) { var params = output.getParameters(); - + for (int i = 0, size = params.size(); i < size; i++) { var p = params.get(i); @@ -182,13 +184,13 @@ public void optimisationVector(ParameterVector output) { var newParam = new Parameter(newId, transform, bounds); newParam.setValue(value); params.add(newParam); - + } } } - + } @Override @@ -259,7 +261,7 @@ public int compareTo(Harmonic o) { @Override public String toString() { - return String.format("[%1d]: f = %3.2f, A = %3.2f, phi = %3.2f", + return String.format("[%1d]: f = %3.2f, A = %3.2f, phi = %3.2f", rank, frequency, amplitude, phaseShift); } diff --git a/src/main/java/pulse/math/LegendrePoly.java b/src/main/java/pulse/math/LegendrePoly.java index b809507..29a82bf 100644 --- a/src/main/java/pulse/math/LegendrePoly.java +++ b/src/main/java/pulse/math/LegendrePoly.java @@ -1,5 +1,6 @@ package pulse.math; +import java.io.Serializable; import static pulse.math.MathUtils.fastPowInt; import static pulse.math.MathUtils.fastPowLoop; import static pulse.properties.NumericProperties.def; @@ -21,8 +22,9 @@ * @see Wiki * page */ -public class LegendrePoly { +public class LegendrePoly implements Serializable { + private static final long serialVersionUID = -6859690814783610846L; private double[] c; private int n; diff --git a/src/main/java/pulse/math/MidpointIntegrator.java b/src/main/java/pulse/math/MidpointIntegrator.java index cc66763..6109de5 100644 --- a/src/main/java/pulse/math/MidpointIntegrator.java +++ b/src/main/java/pulse/math/MidpointIntegrator.java @@ -11,6 +11,8 @@ */ public abstract class MidpointIntegrator extends FixedIntervalIntegrator { + private static final long serialVersionUID = -5434607461290096748L; + public MidpointIntegrator(Segment bounds, NumericProperty segments) { super(bounds, segments); } diff --git a/src/main/java/pulse/math/Parameter.java b/src/main/java/pulse/math/Parameter.java index 5556cd1..b46e02e 100644 --- a/src/main/java/pulse/math/Parameter.java +++ b/src/main/java/pulse/math/Parameter.java @@ -1,12 +1,14 @@ package pulse.math; +import java.io.Serializable; import pulse.math.transforms.Transformable; /** * Parameter class */ -public class Parameter { +public class Parameter implements Serializable { + private static final long serialVersionUID = 3222166682943107207L; private ParameterIdentifier index; private Transformable transform; private Segment bound; @@ -17,9 +19,9 @@ public Parameter(ParameterIdentifier index, Transformable transform, Segment bou this.transform = transform; this.bound = bound; } - + public Parameter(ParameterIdentifier index) { - if(index.getKeyword() != null) { + if (index.getKeyword() != null) { bound = Segment.boundsFrom(index.getKeyword()); } this.index = index; @@ -80,8 +82,8 @@ public double getApparentValue() { public void setValue(double value, boolean ignoreTransform) { this.value = transform == null || ignoreTransform - ? value - : transform.transform(value); + ? value + : transform.transform(value); } public void setValue(double value) { diff --git a/src/main/java/pulse/math/ParameterIdentifier.java b/src/main/java/pulse/math/ParameterIdentifier.java index b96847d..31a66f7 100644 --- a/src/main/java/pulse/math/ParameterIdentifier.java +++ b/src/main/java/pulse/math/ParameterIdentifier.java @@ -1,53 +1,68 @@ package pulse.math; +import java.io.Serializable; +import java.util.Objects; import pulse.properties.NumericPropertyKeyword; -public class ParameterIdentifier { - +public class ParameterIdentifier implements Serializable { + + private static final long serialVersionUID = 5288875329862605319L; private NumericPropertyKeyword keyword; private int index; - + public ParameterIdentifier(NumericPropertyKeyword keyword, int index) { this.keyword = keyword; this.index = index; } - + public ParameterIdentifier(NumericPropertyKeyword keyword) { this(keyword, 0); } - + + @Override + public int hashCode() { + int hash = 7; + hash = 29 * hash + Objects.hashCode(this.keyword); + hash = 29 * hash + this.index; + return hash; + } + public ParameterIdentifier(int index) { this.index = index; } - + public NumericPropertyKeyword getKeyword() { return keyword; } - + public int getIndex() { return index; } - + @Override public boolean equals(Object id) { - if(!id.getClass().equals(ParameterIdentifier.class)) { + if (id.getClass() == null) { return false; } - + + var classA = id.getClass(); + var classB = this.getClass(); + + if (classA != classB) { + return false; + } + var pid = (ParameterIdentifier) id; - - boolean result = true; - - if(keyword != pid.keyword || index != pid.index) - result = false; - - return result; - - } - + return keyword == pid.keyword && Math.abs(index - pid.index) < 1; + } + @Override public String toString() { - return keyword + " # " + index; + StringBuilder sb = new StringBuilder("").append(keyword); + if (index > 0) { + sb.append(" # ").append(index); + } + return sb.toString(); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/math/ParameterVector.java b/src/main/java/pulse/math/ParameterVector.java index 6bd1737..f9d27fb 100644 --- a/src/main/java/pulse/math/ParameterVector.java +++ b/src/main/java/pulse/math/ParameterVector.java @@ -1,9 +1,8 @@ package pulse.math; +import java.io.Serializable; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import pulse.math.linear.Vector; @@ -15,9 +14,10 @@ * A wrapper subclass that assigns {@code ParameterIdentifier}s to specific * components of the vector. Used when constructing the optimisation vector. */ -public class ParameterVector { +public class ParameterVector implements Serializable { - private List params; + private static final long serialVersionUID = -4678286597080149891L; + private final List params; /** * Constructs an {@code IndexedVector} with the specified list of keywords. @@ -109,11 +109,11 @@ public void setValues(Vector v) { throw new IllegalArgumentException("Illegal vector dimension: " + dim + " != " + this.dimension()); } - - for(int i = 0; i < dim; i++) { + + for (int i = 0; i < dim; i++) { params.get(i).setValue(v.get(i)); } - + } public int dimension() { diff --git a/src/main/java/pulse/math/Segment.java b/src/main/java/pulse/math/Segment.java index c1e0c76..95e6c23 100644 --- a/src/main/java/pulse/math/Segment.java +++ b/src/main/java/pulse/math/Segment.java @@ -1,5 +1,6 @@ package pulse.math; +import java.io.Serializable; import java.util.Random; import pulse.properties.NumericPropertyKeyword; import static pulse.properties.NumericProperties.def; @@ -9,11 +10,15 @@ * that {@code a < b}. * */ -public class Segment { +public class Segment implements Serializable { + /** + * + */ + private static final long serialVersionUID = -1373763811823628708L; private double a; private double b; - + public final static Segment UNBOUNDED = new Segment(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); /** @@ -36,17 +41,17 @@ public Segment(Segment segment) { this.a = segment.a; this.b = segment.b; } - + /** - * Creates a segment representing the bounds of {@code p}, i.e. the range - * in which the property value is allowed to change + * Creates a segment representing the bounds of {@code p}, i.e. the range in + * which the property value is allowed to change + * * @param p a property keyword to extract default bounds * @return a {@code Segment} with the bounds */ - public static Segment boundsFrom(NumericPropertyKeyword p) { - return new Segment(def(p).getMinimum().doubleValue(), - def(p).getMaximum().doubleValue()); + return new Segment(def(p).getMinimum().doubleValue(), + def(p).getMaximum().doubleValue()); } /** diff --git a/src/main/java/pulse/math/SimpsonIntegrator.java b/src/main/java/pulse/math/SimpsonIntegrator.java index 55ddda7..8a367d6 100644 --- a/src/main/java/pulse/math/SimpsonIntegrator.java +++ b/src/main/java/pulse/math/SimpsonIntegrator.java @@ -11,6 +11,8 @@ */ public abstract class SimpsonIntegrator extends FixedIntervalIntegrator { + private static final long serialVersionUID = -7800272372472765906L; + public SimpsonIntegrator(Segment bounds) { super(bounds); } diff --git a/src/main/java/pulse/math/Window.java b/src/main/java/pulse/math/Window.java index 48fbd4f..a230ccb 100644 --- a/src/main/java/pulse/math/Window.java +++ b/src/main/java/pulse/math/Window.java @@ -1,60 +1,58 @@ package pulse.math; -public interface Window { +import java.io.Serializable; + +public interface Window extends Serializable { public final static Window NONE = (n, N) -> 1.0; - public final static Window HANN = (n, N) -> Math.pow( Math.sin(Math.PI * n / ((double) N)), 2); - public final static Window HAMMING = (n, N) -> 0.54 + 0.46*Math.cos(2.0 * Math.PI * n / ((double) N)); + public final static Window HANN = (n, N) -> Math.pow(Math.sin(Math.PI * n / ((double) N)), 2); + public final static Window HAMMING = (n, N) -> 0.54 + 0.46 * Math.cos(2.0 * Math.PI * n / ((double) N)); public final static Window BLACKMANN_HARRIS = (n, N) -> { - final double x = 2.0*Math.PI*n/ ((double)N); - return 0.35875 - 0.48829*Math.cos(x) + 0.14128*Math.cos(2.0*x) - 0.01168*Math.cos(3.0*x); - }; - public final static Window FLAT_TOP = (n, N) -> { - final double x = 2.0*Math.PI*n/ ((double)N); - return 0.21557895 - 0.41663158*Math.cos(x) + 0.277263158*Math.cos(2.0*x) - - 0.083578947*Math.cos(3.0*x) + 0.006947368 * Math.cos(4.0 * x); - }; + final double x = 2.0 * Math.PI * n / ((double) N); + return 0.35875 - 0.48829 * Math.cos(x) + 0.14128 * Math.cos(2.0 * x) - 0.01168 * Math.cos(3.0 * x); + }; + public final static Window FLAT_TOP = (n, N) -> { + final double x = 2.0 * Math.PI * n / ((double) N); + return 0.21557895 - 0.41663158 * Math.cos(x) + 0.277263158 * Math.cos(2.0 * x) + - 0.083578947 * Math.cos(3.0 * x) + 0.006947368 * Math.cos(4.0 * x); + }; public final static Window TUKEY = new Window() { - + private final static double alpha = 0.6; - + @Override public double evaluate(int n, int N) { - + double result = 0; - - if(n < 0.5*alpha*N) { - result = 0.5 * ( 1 - Math.cos(2.0*Math.PI*n/(alpha*N))); - } - - else if(n <= N/2) { + + if (n < 0.5 * alpha * N) { + result = 0.5 * (1 - Math.cos(2.0 * Math.PI * n / (alpha * N))); + } else if (n <= N / 2) { result = 1.0; + } else { + result = TUKEY.evaluate(N - n, N); } - - else { - result = TUKEY.evaluate(N - n,N); - } - + return result; - + } }; - + public final static Window HANN_POISSON = (n, N) -> { - + final double alpha = 2.0; - return HANN.evaluate(n, N) * Math.exp( - alpha * (N - 2 * n) / N); - + return HANN.evaluate(n, N) * Math.exp(-alpha * (N - 2 * n) / N); + }; - + public default double[] apply(double[] input) { double[] output = new double[input.length]; - for(int i = 0; i < output.length; i++) { + for (int i = 0; i < output.length; i++) { output[i] = input[i] * evaluate(i, input.length); } return output; } - + public abstract double evaluate(int n, int N); - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/math/ZScore.java b/src/main/java/pulse/math/ZScore.java index fa13fad..83be83a 100644 --- a/src/main/java/pulse/math/ZScore.java +++ b/src/main/java/pulse/math/ZScore.java @@ -17,23 +17,22 @@ * calculated as the difference of the current value and population mean divided * by the population standard deviation. */ - public class ZScore { - + private double[] avgFilter; private double[] stdFilter; private int[] signals; - + private int lag; private double threshold; private double influence; - + public ZScore(int lag, double threshold, double influence) { this.lag = lag; this.threshold = threshold; this.influence = influence; } - + public ZScore() { this(40, 3.5, 0.3); } @@ -42,32 +41,32 @@ public void process(double[] input) { signals = new int[input.length]; List filteredY = DoubleStream.of(input).boxed().collect(Collectors.toList()); - - var initialWindow = filteredY.subList(input.length - lag, input.length - 1); + + var initialWindow = new ArrayList<>(filteredY.subList(input.length - lag, input.length - 1)); avgFilter = new double[input.length]; stdFilter = new double[input.length]; - + avgFilter[input.length - lag + 1] = mean(initialWindow); stdFilter[input.length - lag + 1] = stdev(initialWindow); for (int i = input.length - lag; i > 0; i--) { - + if (Math.abs(input[i] - avgFilter[i + 1]) > threshold * stdFilter[i + 1]) { - + signals[i] = (input[i] > avgFilter[i + 1]) ? 1 : -1; - filteredY.set(i, influence * input[i] - + (1 - influence) * filteredY.get(i + 1)); - + filteredY.set(i, influence * input[i] + + (1 - influence) * filteredY.get(i + 1)); + } else { - + signals[i] = 0; filteredY.set(i, input[i]); - + } // Update rolling average and deviation - var slidingWindow = filteredY.subList(i, i + lag - 1); + var slidingWindow = new ArrayList<>(filteredY.subList(i, i + lag - 1)); avgFilter[i] = mean(slidingWindow); stdFilter[i] = stdev(slidingWindow); @@ -89,19 +88,19 @@ private static double stdev(List values) { } return ret; } - + public int[] getSignals() { return signals; } - + public double[] getFilteredAverage() { return avgFilter; } - + public double[] getFilteredStdev() { return stdFilter; } - + /* public static void main(String[] args) { Scanner sc = null; @@ -129,6 +128,5 @@ public static void main(String[] args) { } } - */ - -} \ No newline at end of file + */ +} diff --git a/src/main/java/pulse/math/filters/AssignmentListener.java b/src/main/java/pulse/math/filters/AssignmentListener.java index 251884a..6020aa6 100644 --- a/src/main/java/pulse/math/filters/AssignmentListener.java +++ b/src/main/java/pulse/math/filters/AssignmentListener.java @@ -1,7 +1,9 @@ package pulse.math.filters; -public interface AssignmentListener { - +import java.io.Serializable; + +public interface AssignmentListener extends Serializable { + public void onValueAssigned(); - + } \ No newline at end of file diff --git a/src/main/java/pulse/math/filters/Filter.java b/src/main/java/pulse/math/filters/Filter.java index 067ab3e..0bba2ae 100644 --- a/src/main/java/pulse/math/filters/Filter.java +++ b/src/main/java/pulse/math/filters/Filter.java @@ -1,14 +1,16 @@ package pulse.math.filters; import java.awt.geom.Point2D; +import java.io.Serializable; import java.util.List; import pulse.DiscreteInput; -public interface Filter { +public interface Filter extends Serializable { + + public List process(List input); - public List process(List input); public default List process(DiscreteInput input) { return process(DiscreteInput.convert(input.getX(), input.getY())); - } - -} \ No newline at end of file + } + +} diff --git a/src/main/java/pulse/math/filters/HalfTimeCalculator.java b/src/main/java/pulse/math/filters/HalfTimeCalculator.java index e2ca9ce..7f98f3e 100644 --- a/src/main/java/pulse/math/filters/HalfTimeCalculator.java +++ b/src/main/java/pulse/math/filters/HalfTimeCalculator.java @@ -1,6 +1,7 @@ package pulse.math.filters; import java.awt.geom.Point2D; +import java.io.Serializable; import static java.lang.Double.valueOf; import static java.util.Collections.max; import java.util.Comparator; @@ -9,20 +10,21 @@ import pulse.baseline.FlatBaseline; import pulse.input.IndexRange; -public class HalfTimeCalculator { - +public class HalfTimeCalculator implements Serializable { + + private static final long serialVersionUID = 8302980290467110065L; private final Filter filter; private final DiscreteInput data; private Point2D max; private double halfTime; - + /** * A fail-safe factor. */ public final static double FAIL_SAFE_FACTOR = 10.0; - private static final Comparator pointComparator = - (p1, p2) -> valueOf(p1.getY()).compareTo(valueOf(p2.getY())); + private static final Comparator pointComparator + = (p1, p2) -> valueOf(p1.getY()).compareTo(valueOf(p2.getY())); public HalfTimeCalculator(DiscreteInput input) { this.data = input; @@ -40,56 +42,55 @@ public HalfTimeCalculator(DiscreteInput input) { * The index corresponding to the closest temperature value available for * that curve is used to retrieve the half-rise time (which also has the * same index). If this fails, i.e. the associated index is less than 1, - * this will print out a warning message and still assign a value to the - * half-time variable equal to the acquisition time divided by a fail-safe factor - * {@value FAIL_SAFE_FACTOR}. - *

+ * this will print out a warning message and still assign a value to the + * half-time variable equal to the acquisition time divided by a fail-safe + * factor {@value FAIL_SAFE_FACTOR}. + *

+ * * @see getHalfTime() */ public void calculate() { var baseline = new FlatBaseline(); baseline.fitTo(data); - + var filtered = filter.process(data); - + max = max(filtered, pointComparator); - double halfMax = (max.getY() + baseline.valueAt(0)) / 2.0; - + double halfMax = (max.getY() + baseline.valueAt(0)) / 2.0; + int indexLeft = IndexRange.closestLeft(halfMax, - filtered.stream().map(point -> point.getY()) - .collect(Collectors.toList())); - + filtered.stream().map(point -> point.getY()) + .collect(Collectors.toList())); + if (indexLeft < 1 || indexLeft > filtered.size() - 2) { halfTime = filtered.get(filtered.size() - 1).getX() / FAIL_SAFE_FACTOR; - } - else { + } else { //extrapolate Point2D p1 = filtered.get(indexLeft); Point2D p2 = filtered.get(indexLeft + 1); - - halfTime = (halfMax - p1.getY())/(p2.getY() - p1.getY()) - *(p2.getX() - p1.getX()) + p1.getX(); + + halfTime = (halfMax - p1.getY()) / (p2.getY() - p1.getY()) + * (p2.getX() - p1.getX()) + p1.getX(); } - + } - - + /** - * Retrieves the half-time value of this dataset, which is equal to the - * time needed to reach half of the signal maximum. + * Retrieves the half-time value of this dataset, which is equal to the time + * needed to reach half of the signal maximum. + * * @return the half-time value. */ - public final double getHalfTime() { return halfTime; } - + public final Point2D getFilteredMaximum() { return max; } - + public DiscreteInput getData() { return data; } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/math/filters/OptimisablePolyline.java b/src/main/java/pulse/math/filters/OptimisablePolyline.java index 4d7a8d6..4356b4f 100644 --- a/src/main/java/pulse/math/filters/OptimisablePolyline.java +++ b/src/main/java/pulse/math/filters/OptimisablePolyline.java @@ -14,9 +14,10 @@ public class OptimisablePolyline extends PropertyHolder implements Optimisable { + private static final long serialVersionUID = 418264754603533971L; private final double[] x; private final double[] y; - private final List listeners; + private List listeners; public OptimisablePolyline(List data) { x = data.stream().mapToDouble(d -> d.getX()).toArray(); @@ -27,7 +28,7 @@ public OptimisablePolyline(List data) { @Override public void assign(ParameterVector input) throws SolverException { var ps = input.getParameters(); - for(int i = 0, size = ps.size(); i < size; i++) { + for (int i = 0, size = ps.size(); i < size; i++) { y[i] = ps.get(i).getApparentValue(); } listeners.stream().forEach(l -> l.onValueAssigned()); @@ -37,7 +38,7 @@ public void assign(ParameterVector input) throws SolverException { public void optimisationVector(ParameterVector output) { output.setValues(new Vector(y)); } - + public List points() { return DiscreteInput.convert(x, y); } @@ -46,17 +47,17 @@ public List points() { public void set(NumericPropertyKeyword type, NumericProperty property) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } - + public double[] getX() { return x; } - + public double[] getY() { return y; } - + public void addAssignmentListener(AssignmentListener listener) { listeners.add(listener); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/math/filters/OptimisedRunningAverage.java b/src/main/java/pulse/math/filters/OptimisedRunningAverage.java index 46c004b..0f9ce5a 100644 --- a/src/main/java/pulse/math/filters/OptimisedRunningAverage.java +++ b/src/main/java/pulse/math/filters/OptimisedRunningAverage.java @@ -6,6 +6,8 @@ public class OptimisedRunningAverage extends RunningAverage { + private static final long serialVersionUID = 1272276960302188392L; + public OptimisedRunningAverage() { super(); } @@ -17,10 +19,10 @@ public OptimisedRunningAverage(int reductionFactor) { @Override public List process(DiscreteInput input) { var p = super.process(input); - var optimisableCurve = new OptimisablePolyline(p); + var optimisableCurve = new OptimisablePolyline(p); var task = new PolylineOptimiser(input, optimisableCurve); task.run(); return optimisableCurve.points(); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/math/filters/PolylineOptimiser.java b/src/main/java/pulse/math/filters/PolylineOptimiser.java index dde0142..c2f7ffb 100644 --- a/src/main/java/pulse/math/filters/PolylineOptimiser.java +++ b/src/main/java/pulse/math/filters/PolylineOptimiser.java @@ -20,6 +20,7 @@ public class PolylineOptimiser extends SimpleOptimisationTask { + private static final long serialVersionUID = -9056678836812293655L; private final OptimiserStatistic sos; private final PolylineResponse response; private final OptimisablePolyline optimisableCurve; @@ -87,4 +88,4 @@ public double evaluate(double t) { } } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/math/filters/Randomiser.java b/src/main/java/pulse/math/filters/Randomiser.java index 3d15453..cfd6d71 100644 --- a/src/main/java/pulse/math/filters/Randomiser.java +++ b/src/main/java/pulse/math/filters/Randomiser.java @@ -4,23 +4,24 @@ import java.util.List; public class Randomiser implements Filter { - + + private static final long serialVersionUID = 3390706390237573886L; private final double amplitude; - + public Randomiser(double amplitude) { this.amplitude = amplitude; } @Override - public List process(List input) { - input.forEach(p -> - ((Point2D.Double)p).y += (Math.random() - 0.5) * amplitude + public List process(List input) { + input.forEach(p + -> ((Point2D.Double) p).y += (Math.random() - 0.5) * amplitude ); return input; } - + public double getAmplitude() { return amplitude; } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/math/filters/RunningAverage.java b/src/main/java/pulse/math/filters/RunningAverage.java index 37a08a2..2a2b82b 100644 --- a/src/main/java/pulse/math/filters/RunningAverage.java +++ b/src/main/java/pulse/math/filters/RunningAverage.java @@ -3,30 +3,29 @@ import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; -import pulse.DiscreteInput; public class RunningAverage implements Filter { - + + private static final long serialVersionUID = -6134297308468858848L; + private int bins; - - /** + + /** * The binning factor used to build a crude approximation of the heating * curve. Described in Lunev, A., & Heymer, R. (2020). Review of * Scientific Instruments, 91(6), 064902. */ - public static final int DEFAULT_BINS = 16; public final static int MIN_BINS = 4; - + /** * @param reductionFactor the factor, by which the number of points * {@code count} will be reduced for this {@code ExperimentalData}. */ - public RunningAverage(int reductionFactor) { this.bins = reductionFactor; } - + public RunningAverage() { this.bins = DEFAULT_BINS; } @@ -53,7 +52,6 @@ public RunningAverage() { * @see halfRiseTime() * @see pulse.AbstractData.maxTemperature() */ - @Override public List process(List points) { var x = points.stream().mapToDouble(p -> p.getX()).toArray(); @@ -62,14 +60,14 @@ public List process(List points) { int size = x.length; int step = size / bins; List movingAverage = new ArrayList<>(bins); - + for (int i = 0; i < bins; i++) { - int i1 = step*i; - int i2 = step*(i+1); + int i1 = step * i; + int i2 = step * (i + 1); double av = 0; int j; - + for (j = i1; j < i2 && j < size; j++) { av += y[j]; } @@ -77,55 +75,54 @@ public List process(List points) { av /= j - i1; i2 = j - 1; - movingAverage.add(new Point2D.Double( - (x[i1] + x[i2])/ 2.0, av)); + movingAverage.add(new Point2D.Double( + (x[i1] + x[i2]) / 2.0, av)); } - + addBoundaryPoints(movingAverage, x[0], x[size - 1]); - + /* for(int i = 0; i < movingAverage.size(); i++) { System.err.println(movingAverage.get(i)); } - */ - + */ return movingAverage; - } - + } + private static void addBoundaryPoints(List d, double minTime, double maxTime) { int max = d.size(); - + d.add( - extrapolate(d.get(max - 1), - d.get(max - 2), - maxTime) - ); - - d.add( 0, - extrapolate(d.get(0), - d.get(1), - minTime) - ); - + extrapolate(d.get(max - 1), + d.get(max - 2), + maxTime) + ); + + d.add(0, + extrapolate(d.get(0), + d.get(1), + minTime) + ); + } - - private static Point2D extrapolate(Point2D a, Point2D b, double x) { + + private static Point2D extrapolate(Point2D a, Point2D b, double x) { double y1 = a.getY(); double y2 = b.getY(); double x1 = a.getX(); double x2 = b.getX(); - - return new Point2D.Double(x, y1 + (x - x1)/(x2 - x1)*(y2 - y1)); + + return new Point2D.Double(x, y1 + (x - x1) / (x2 - x1) * (y2 - y1)); } - + public final int getNumberOfBins() { return bins; } - + public final void setNumberOfBins(int no) { this.bins = no > MIN_BINS - 1 ? no : MIN_BINS; } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/math/linear/Matrix2.java b/src/main/java/pulse/math/linear/Matrix2.java index aa57984..00cf409 100644 --- a/src/main/java/pulse/math/linear/Matrix2.java +++ b/src/main/java/pulse/math/linear/Matrix2.java @@ -6,6 +6,8 @@ */ class Matrix2 extends SquareMatrix { + private static final long serialVersionUID = 6015187791989387058L; + protected Matrix2(double[][] args) { super(args); } diff --git a/src/main/java/pulse/math/linear/Matrix3.java b/src/main/java/pulse/math/linear/Matrix3.java index 09e799f..3835e32 100644 --- a/src/main/java/pulse/math/linear/Matrix3.java +++ b/src/main/java/pulse/math/linear/Matrix3.java @@ -6,6 +6,8 @@ */ class Matrix3 extends SquareMatrix { + private static final long serialVersionUID = -2671066600560428989L; + protected Matrix3(double[][] args) { super(args); } diff --git a/src/main/java/pulse/math/linear/Matrix4.java b/src/main/java/pulse/math/linear/Matrix4.java index 970a7a9..cc34b17 100644 --- a/src/main/java/pulse/math/linear/Matrix4.java +++ b/src/main/java/pulse/math/linear/Matrix4.java @@ -6,6 +6,8 @@ */ class Matrix4 extends SquareMatrix { + private static final long serialVersionUID = -1355372261335732541L; + protected Matrix4(double[][] args) { super(args); } diff --git a/src/main/java/pulse/math/linear/RectangularMatrix.java b/src/main/java/pulse/math/linear/RectangularMatrix.java index 8f325dc..8ac8470 100644 --- a/src/main/java/pulse/math/linear/RectangularMatrix.java +++ b/src/main/java/pulse/math/linear/RectangularMatrix.java @@ -1,5 +1,6 @@ package pulse.math.linear; +import java.io.Serializable; import static pulse.math.MathUtils.approximatelyEquals; import static pulse.math.linear.ArithmeticOperations.DIFFERENCE; import static pulse.math.linear.ArithmeticOperations.SUM; @@ -7,8 +8,9 @@ import pulse.ui.Messages; -public class RectangularMatrix { +public class RectangularMatrix implements Serializable { + private static final long serialVersionUID = -8184303238440935851L; protected final double[][] x; protected RectangularMatrix(double[][] args) { diff --git a/src/main/java/pulse/math/linear/Vector.java b/src/main/java/pulse/math/linear/Vector.java index 39d0653..483cf0c 100644 --- a/src/main/java/pulse/math/linear/Vector.java +++ b/src/main/java/pulse/math/linear/Vector.java @@ -1,5 +1,6 @@ package pulse.math.linear; +import java.io.Serializable; import static java.lang.Math.abs; import static java.lang.Math.sqrt; import java.util.List; @@ -16,8 +17,12 @@ * and ODE solvers. *

*/ -public class Vector { +public class Vector implements Serializable { + /** + * + */ + private static final long serialVersionUID = 5560069982536341831L; private double[] x; /** @@ -122,7 +127,7 @@ public static Vector random(int n, double min, double max) { } return v; } - + /** * Component-wise vector multiplication */ diff --git a/src/main/java/pulse/math/transforms/AtanhTransform.java b/src/main/java/pulse/math/transforms/AtanhTransform.java index b65577e..253fd0e 100644 --- a/src/main/java/pulse/math/transforms/AtanhTransform.java +++ b/src/main/java/pulse/math/transforms/AtanhTransform.java @@ -11,6 +11,8 @@ */ public class AtanhTransform extends BoundedParameterTransform { + private static final long serialVersionUID = -6322775329000050307L; + /** * Only the upper bound of the argument is used. * diff --git a/src/main/java/pulse/math/transforms/InvDiamTransform.java b/src/main/java/pulse/math/transforms/InvDiamTransform.java index c881081..22345df 100644 --- a/src/main/java/pulse/math/transforms/InvDiamTransform.java +++ b/src/main/java/pulse/math/transforms/InvDiamTransform.java @@ -8,6 +8,7 @@ */ public class InvDiamTransform implements Transformable { + private static final long serialVersionUID = 1809584085307619279L; private double d; public InvDiamTransform(ExtendedThermalProperties etp) { diff --git a/src/main/java/pulse/math/transforms/PeriodicTransform.java b/src/main/java/pulse/math/transforms/PeriodicTransform.java index 31cee06..c788a9b 100644 --- a/src/main/java/pulse/math/transforms/PeriodicTransform.java +++ b/src/main/java/pulse/math/transforms/PeriodicTransform.java @@ -4,6 +4,8 @@ public class PeriodicTransform extends BoundedParameterTransform { + private static final long serialVersionUID = 4564881912462997982L; + /** * Only the upper bound of the argument is used. * @@ -23,7 +25,7 @@ public double transform(double a) { double max = getBounds().getMaximum(); double min = getBounds().getMinimum(); double len = max - min; - + return a > max ? transform(a - len) : (a < min ? transform(a + len) : a); } @@ -35,4 +37,4 @@ public double transform(double a) { public double inverse(double t) { return t; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/math/transforms/StickTransform.java b/src/main/java/pulse/math/transforms/StickTransform.java index f548761..ce5fd4a 100644 --- a/src/main/java/pulse/math/transforms/StickTransform.java +++ b/src/main/java/pulse/math/transforms/StickTransform.java @@ -18,16 +18,18 @@ import pulse.math.Segment; /** - * A simple bounded transform which makes the parameter stick to the - * boundaries upon reaching them. For insatnce, when a parameter x + * A simple bounded transform which makes the parameter stick to the boundaries + * upon reaching them. For insatnce, when a parameter x * attempts to escape its bounds due to a larger increment then allowed, this * transform will return it directly to the respective boundary, where it will * "stick". + * * @author Artem Lunev */ - public class StickTransform extends BoundedParameterTransform { + private static final long serialVersionUID = -8709273330809657074L; + /** * Only the upper bound of the argument is used. * @@ -57,5 +59,5 @@ public double transform(double a) { public double inverse(double t) { return transform(t); } - + } diff --git a/src/main/java/pulse/math/transforms/Transformable.java b/src/main/java/pulse/math/transforms/Transformable.java index 88e9d13..150f6c3 100644 --- a/src/main/java/pulse/math/transforms/Transformable.java +++ b/src/main/java/pulse/math/transforms/Transformable.java @@ -1,11 +1,13 @@ package pulse.math.transforms; +import java.io.Serializable; + /** * An interface for performing reversible one-to-one mapping of the model * parameters. * */ -public interface Transformable { +public interface Transformable extends Serializable { /** * Performs the selected transform with {@code value} diff --git a/src/main/java/pulse/problem/laser/DiscretePulse.java b/src/main/java/pulse/problem/laser/DiscretePulse.java index e1a283f..5ec9be1 100644 --- a/src/main/java/pulse/problem/laser/DiscretePulse.java +++ b/src/main/java/pulse/problem/laser/DiscretePulse.java @@ -1,5 +1,6 @@ package pulse.problem.laser; +import java.io.Serializable; import java.util.Objects; import pulse.input.ExperimentalData; import pulse.math.MidpointIntegrator; @@ -7,7 +8,10 @@ import pulse.problem.schemes.Grid; import pulse.problem.statements.Problem; import pulse.problem.statements.Pulse; +import static pulse.properties.NumericProperties.derive; +import static pulse.properties.NumericPropertyKeyword.TAU_FACTOR; import pulse.tasks.SearchTask; +import pulse.util.PropertyHolderListener; /** * A {@code DiscretePulse} is an object that acts as a medium between the @@ -16,13 +20,15 @@ * * @see pulse.problem.statements.Pulse */ -public class DiscretePulse { +public class DiscretePulse implements Serializable { + private static final long serialVersionUID = 5826506918603729615L; private final Grid grid; private final Pulse pulse; - + private final ExperimentalData data; + private double widthOnGrid; - private double timeConversionFactor; + private double characteristicTime; private double invTotalEnergy; //normalisation factor /** @@ -49,33 +55,28 @@ public class DiscretePulse { */ public DiscretePulse(Problem problem, Grid grid) { this.grid = grid; - timeConversionFactor = problem.getProperties().timeFactor(); + characteristicTime = problem.getProperties().characteristicTime(); this.pulse = problem.getPulse(); Object ancestor = Objects.requireNonNull(problem.specificAncestor(SearchTask.class), "Problem has not been assigned to a SearchTask"); - ExperimentalData data = - (ExperimentalData) ( ((SearchTask) ancestor).getInput() ); - init(data); - + data = (ExperimentalData) (((SearchTask) ancestor).getInput()); + init(); + + PropertyHolderListener phl = e -> { + characteristicTime = problem.getProperties().characteristicTime(); + widthOnGrid = 0; + init(); + }; + pulse.addListener(e -> { - timeConversionFactor = problem.getProperties().timeFactor(); - init(data); + widthOnGrid = 0; + init(); }); + problem.addListener(phl); - grid.addListener(e - -> init(data) - ); - - } - - private void init(ExperimentalData data) { - widthOnGrid = 0; - recalculate(); - pulse.getPulseShape().init(data, this); - invTotalEnergy = 1.0/totalEnergy(); } /** @@ -95,39 +96,77 @@ public double laserPowerAt(double time) { * * @see pulse.problem.schemes.Grid.gridTime(double,double) */ - public final void recalculate() { - final double nominalWidth = ((Number) pulse.getPulseWidth().getValue()).doubleValue(); - final double resolvedWidth = timeConversionFactor / getWidthToleranceFactor(); + public final void init() { + final double nominalWidth = ((Number) pulse.getPulseWidth().getValue()).doubleValue(); + final double resolvedWidth = resolvedPulseWidthSeconds(); final double EPS = 1E-10; - + + double oldValue = widthOnGrid; + this.widthOnGrid = pulseWidthGrid(); + /** * The pulse is too short, which makes calculations too expensive. Can * we replace it with a rectangular pulse shape instead? */ - - if (nominalWidth < resolvedWidth - EPS && widthOnGrid < EPS) { + if (nominalWidth < resolvedWidth - EPS && oldValue < EPS) { //change shape to rectangular var shape = new RectangularPulse(); - pulse.setPulseShape(shape); - //change pulse width - setDiscreteWidth(resolvedWidth); + pulse.setPulseShape(shape); shape.init(null, this); - //adjust the pulse object to update the visualised pulse - } else if(nominalWidth > resolvedWidth + EPS) { - setDiscreteWidth(nominalWidth); - } - - invTotalEnergy = 1.0/totalEnergy(); - + } else { + pulse.getPulseShape().init(data, this); + } + + invTotalEnergy = 1.0 / totalEnergy(); } - + /** - * Calculates the total pulse energy using a numerical integrator.The - * normalisation factor is then equal to the inverse total energy. - * @return the total pulse energy, assuming sample area fully covered by the beam + * Optimises the {@code Grid} parameters so that the timestep is + * sufficiently small to enable accurate pulse correction. + *

+ * This can change the {@code tauFactor} and {@code tau} variables in the + * {@code Grid} object if {@code discretePulseWidth/(M - 1) < grid.tau}, + * where M is the required number of pulse calculations. + *

+ * + * @see PulseTemporalShape.getRequiredDiscretisation() */ + public double pulseWidthGrid() { + //minimum number of points for pulse calculation + int reqPoints = pulse.getPulseShape().getRequiredDiscretisation(); + //physical pulse width in time units + double experimentalWidth = (double) pulse.getPulseWidth().getValue(); + + //minimum resolved pulse width in time units for that specific problem + double resolvedWidth = resolvedPulseWidthSeconds(); + + double pWidth = Math.max(experimentalWidth, resolvedWidth); + + final double EPS = 1E-10; + double newTau = pWidth / characteristicTime / reqPoints; + + double result = 0; + + if (newTau < grid.getTimeStep() - EPS) { + double newTauFactor = (double) grid.getTimeFactor().getValue() / 2.0; + grid.setTimeFactor(derive(TAU_FACTOR, newTauFactor)); + result = pulseWidthGrid(); + } else { + result = grid.gridTime(pWidth, characteristicTime); + } + + return result; + } + + /** + * Calculates the total pulse energy using a numerical integrator.The + * normalisation factor is then equal to the inverse total energy. + * + * @return the total pulse energy, assuming sample area fully covered by the + * beam + */ public final double totalEnergy() { var pulseShape = pulse.getPulseShape(); @@ -144,27 +183,22 @@ public double integrand(double... vars) { } /** - * Gets the discrete dimensionless pulse width, which is a multiplier of the current - * grid timestep. The pulse width is converted to the dimensionless pulse width by - * dividing the real value by l2/a. + * Gets the discrete dimensionless pulse width, which is a multiplier of the + * current grid timestep. The pulse width is converted to the dimensionless + * pulse width by dividing the real value by l2/a. * * @return the dimensionless pulse width mapped to the grid. */ public double getDiscreteWidth() { return widthOnGrid; } - - private void setDiscreteWidth(double width) { - widthOnGrid = grid.gridTime(width, timeConversionFactor); - grid.adjustTimeStep(this); - } /** * Gets the physical {@code Pulse} * * @return the {@code Pulse} object */ - public Pulse getPulse() { + public Pulse getPhysicalPulse() { return pulse; } @@ -176,36 +210,38 @@ public Pulse getPulse() { public Grid getGrid() { return grid; } - + /** - * Gets the dimensional factor required to convert real time variable into - * a dimensional variable, defined in the {@code Problem} class + * Gets the dimensional factor required to convert real time variable into a + * dimensional variable, defined in the {@code Problem} class + * * @return the conversion factor */ - - public double getConversionFactor() { - return timeConversionFactor; + public double getCharacteristicTime() { + return characteristicTime; } - + /** - * Gets the minimal resolved pulse width defined by the {@code WIDTH_TOLERANCE_FACTOR} - * and the characteristic time given by the {@code getConversionFactor}. - * @return + * Gets the minimal resolved pulse width defined by the + * {@code WIDTH_TOLERANCE_FACTOR} and the characteristic time given by the + * {@code getConversionFactor}. + * + * @return */ - - public double resolvedPulseWidth() { - return timeConversionFactor / getWidthToleranceFactor(); + public double resolvedPulseWidthSeconds() { + return characteristicTime / getWidthToleranceFactor(); } - - /** - * Assuming a characteristic time is divided by the return value of this method - * and is set to the minimal resolved pulse width, shows how small a pulse width - * can be to enable finite pulse correction. - * @return the smallest fraction of a characteristic time resolved as a finite pulse. + + /** + * Assuming a characteristic time is divided by the return value of this + * method and is set to the minimal resolved pulse width, shows how small a + * pulse width can be to enable finite pulse correction. + * + * @return the smallest fraction of a characteristic time resolved as a + * finite pulse. */ - public int getWidthToleranceFactor() { return WIDTH_TOLERANCE_FACTOR; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/laser/DiscretePulse2D.java b/src/main/java/pulse/problem/laser/DiscretePulse2D.java index ab9fd0e..21be2b8 100644 --- a/src/main/java/pulse/problem/laser/DiscretePulse2D.java +++ b/src/main/java/pulse/problem/laser/DiscretePulse2D.java @@ -18,15 +18,15 @@ */ public class DiscretePulse2D extends DiscretePulse { + private static final long serialVersionUID = 6203222036852037146L; private double discretePulseSpot; private double sampleRadius; private double normFactor; - + /** * This had to be decreased for the 2d pulses. */ - - private final static int WIDTH_TOLERANCE_FACTOR = 200; + private final static int WIDTH_TOLERANCE_FACTOR = 1000; /** * The constructor for {@code DiscretePulse2D}. @@ -43,9 +43,9 @@ public DiscretePulse2D(ClassicalProblem2D problem, Grid2D grid) { super(problem, grid); var properties = (ExtendedThermalProperties) problem.getProperties(); calcPulseSpot(properties); - properties.addListener(e -> calcPulseSpot(properties) ); + properties.addListener(e -> calcPulseSpot(properties)); } - + /** * This calculates the dimensionless, discretised pulse function at a * dimensionless radial coordinate {@code coord}. @@ -61,58 +61,58 @@ public DiscretePulse2D(ClassicalProblem2D problem, Grid2D grid) { * {@code coord > spotDiameter}. * @see pulse.problem.laser.PulseTemporalShape.laserPowerAt(double) */ - public double evaluateAt(double time, double radialCoord) { - return laserPowerAt(time) + return laserPowerAt(time) * (0.5 + 0.5 * signum(discretePulseSpot - radialCoord)); } - + /** - * Calculates the laser power at a give moment in time. The total laser - * energy is normalised over a beam partially illuminating the sample surface. + * Calculates the laser power at a give moment in time. The total laser + * energy is normalised over a beam partially illuminating the sample + * surface. + * * @param time a moment in time (in dimensionless units) * @return the laser power in arbitrary units */ - @Override public double laserPowerAt(double time) { return normFactor * super.laserPowerAt(time); } - + private void calcPulseSpot(ExtendedThermalProperties properties) { - sampleRadius = (double) properties.getSampleDiameter().getValue() / 2.0; + sampleRadius = (double) properties.getSampleDiameter().getValue() / 2.0; evalPulseSpot(); } /** - * Calculates the {@code discretePulseSpot} using the {@code gridRadialDistance} method. + * Calculates the {@code discretePulseSpot} using the + * {@code gridRadialDistance} method. * * @see pulse.problem.schemes.Grid2D.gridRadialDistance(double,double) */ public final void evalPulseSpot() { - var pulse = (Pulse2D) getPulse(); + var pulse = (Pulse2D) getPhysicalPulse(); var grid2d = (Grid2D) getGrid(); - final double spotRadius = (double) pulse.getSpotDiameter().getValue() / 2.0; + final double spotRadius = (double) pulse.getSpotDiameter().getValue() / 2.0; discretePulseSpot = grid2d.gridRadialDistance(spotRadius, sampleRadius); grid2d.adjustStepSize(this); - normFactor = sampleRadius * sampleRadius / spotRadius / spotRadius; + normFactor = sampleRadius * sampleRadius / spotRadius / spotRadius; } public final double getDiscretePulseSpot() { return discretePulseSpot; } - + public final double getRadialConversionFactor() { return sampleRadius; } - + /** * A smaller tolerance factor is set for 2D calculations */ - @Override public int getWidthToleranceFactor() { return WIDTH_TOLERANCE_FACTOR; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/laser/ExponentiallyModifiedGaussian.java b/src/main/java/pulse/problem/laser/ExponentiallyModifiedGaussian.java index 379ab97..d4490d2 100644 --- a/src/main/java/pulse/problem/laser/ExponentiallyModifiedGaussian.java +++ b/src/main/java/pulse/problem/laser/ExponentiallyModifiedGaussian.java @@ -24,10 +24,11 @@ */ public class ExponentiallyModifiedGaussian extends PulseTemporalShape { + private static final long serialVersionUID = -4437794069527301235L; private double mu; private double sigma; private double lambda; - + private final static int MIN_POINTS = 10; /** @@ -159,4 +160,4 @@ public int getRequiredDiscretisation() { return MIN_POINTS; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/laser/NumericPulse.java b/src/main/java/pulse/problem/laser/NumericPulse.java index e834f02..a71d65f 100644 --- a/src/main/java/pulse/problem/laser/NumericPulse.java +++ b/src/main/java/pulse/problem/laser/NumericPulse.java @@ -1,10 +1,14 @@ package pulse.problem.laser; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericPropertyKeyword.PULSE_WIDTH; import org.apache.commons.math3.analysis.UnivariateFunction; import org.apache.commons.math3.analysis.interpolation.AkimaSplineInterpolator; +import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction; import pulse.input.ExperimentalData; import pulse.problem.statements.Problem; @@ -14,6 +18,7 @@ import pulse.baseline.FlatBaseline; import pulse.tasks.Calculation; +import pulse.util.FunctionSerializer; /** * A numeric pulse is given by a set of discrete {@code NumericPulseData} @@ -24,9 +29,10 @@ */ public class NumericPulse extends PulseTemporalShape { + private static final long serialVersionUID = 6088261629992349844L; private NumericPulseData pulseData; - private UnivariateFunction interpolation; - + private transient UnivariateFunction interpolation; + private final static int MIN_POINTS = 20; public NumericPulse() { @@ -57,40 +63,42 @@ public NumericPulse(NumericPulse pulse) { public void init(ExperimentalData data, DiscretePulse pulse) { //generate baseline-subtracted numeric data from ExperimentalData baselineSubtractedFrom(data); - + //notify host pulse object of a new pulse width - var problem = ( (Calculation) ((SearchTask) data.getParent()) - .getResponse() ).getProblem(); + var problem = ((Calculation) ((SearchTask) data.getParent()) + .getResponse()).getProblem(); setPulseWidthOf(problem); //convert to dimensionless time and interpolate - double timeFactor = problem.getProperties().timeFactor(); + double timeFactor = problem.getProperties().characteristicTime(); doInterpolation(timeFactor); } - + /** - * Copies the numeric pulse from metadata and subtracts a horizontal baseline - * from the data points assigned to {@code pulseData}. - * @param data the experimental data containing the metadata with numeric pulse data. + * Copies the numeric pulse from metadata and subtracts a horizontal + * baseline from the data points assigned to {@code pulseData}. + * + * @param data the experimental data containing the metadata with numeric + * pulse data. */ - private void baselineSubtractedFrom(ExperimentalData data) { pulseData = new NumericPulseData(data.getMetadata().getPulseData()); - + //subtracts a horizontal baseline from the pulse data var baseline = new FlatBaseline(); baseline.fitTo(pulseData); - - for(int i = 0, size = pulseData.getTimeSequence().size(); i < size; i++) - pulseData.setSignalAt(i, + + for (int i = 0, size = pulseData.getTimeSequence().size(); i < size; i++) { + pulseData.setSignalAt(i, pulseData.signalAt(i) - baseline.valueAt(pulseData.timeAt(i))); + } } private void setPulseWidthOf(Problem problem) { - var timeSequence = pulseData.getTimeSequence(); - double pulseWidth = timeSequence.get(timeSequence.size() - 1); + var timeSequence = pulseData.getTimeSequence(); + double pulseWidth = timeSequence.get(timeSequence.size() - 1); - var pulseObject = problem.getPulse(); + var pulseObject = problem.getPulse(); pulseObject.setPulseWidth(derive(PULSE_WIDTH, pulseWidth)); } @@ -98,11 +106,11 @@ private void setPulseWidthOf(Problem problem) { private void doInterpolation(double timeFactor) { var interpolator = new AkimaSplineInterpolator(); - var timeList = pulseData.getTimeSequence().stream().mapToDouble(d -> d / timeFactor).toArray(); - var powerList = pulseData.getSignalData(); + var timeList = pulseData.getTimeSequence().stream().mapToDouble(d -> d / timeFactor).toArray(); + var powerList = pulseData.getSignalData(); this.setPulseWidth(timeList[timeList.length - 1]); - + interpolation = interpolator.interpolate(timeList, powerList.stream().mapToDouble(d -> d).toArray()); } @@ -135,7 +143,7 @@ public NumericPulseData getData() { public void setData(NumericPulseData pulseData) { this.pulseData = pulseData; - + } public UnivariateFunction getInterpolation() { @@ -147,4 +155,22 @@ public int getRequiredDiscretisation() { return MIN_POINTS; } + /* + Serialization + */ + private void writeObject(ObjectOutputStream oos) + throws IOException { + // default serialization + oos.defaultWriteObject(); + // write the object + FunctionSerializer.writeSplineFunction((PolynomialSplineFunction) interpolation, oos); + } + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + // default deserialization + ois.defaultReadObject(); + this.interpolation = FunctionSerializer.readSplineFunction(ois); + } + } diff --git a/src/main/java/pulse/problem/laser/NumericPulseData.java b/src/main/java/pulse/problem/laser/NumericPulseData.java index 5a53fd0..7bbfeee 100644 --- a/src/main/java/pulse/problem/laser/NumericPulseData.java +++ b/src/main/java/pulse/problem/laser/NumericPulseData.java @@ -14,6 +14,7 @@ */ public class NumericPulseData extends AbstractData implements DiscreteInput { + private static final long serialVersionUID = 8142129124831241206L; private final int externalID; /** @@ -54,7 +55,7 @@ public void addPoint(double time, double power) { public int getExternalID() { return externalID; } - + public double pulseWidth() { return super.timeLimit(); } @@ -73,5 +74,5 @@ public List getY() { public IndexRange getIndexRange() { return new IndexRange(this.getTimeSequence(), Range.UNLIMITED); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/problem/laser/PulseTemporalShape.java b/src/main/java/pulse/problem/laser/PulseTemporalShape.java index 6c3814e..9a74c09 100644 --- a/src/main/java/pulse/problem/laser/PulseTemporalShape.java +++ b/src/main/java/pulse/problem/laser/PulseTemporalShape.java @@ -1,6 +1,5 @@ package pulse.problem.laser; - import pulse.input.ExperimentalData; import pulse.util.PropertyHolder; import pulse.util.Reflexive; @@ -23,7 +22,7 @@ public PulseTemporalShape() { public PulseTemporalShape(PulseTemporalShape another) { this.width = another.width; } - + /** * This evaluates the dimensionless, discretised pulse function on a * {@code grid} needed to evaluate the heat source in the difference scheme. @@ -64,7 +63,7 @@ public double getPulseWidth() { public void setPulseWidth(double width) { this.width = width; } - + public abstract int getRequiredDiscretisation(); -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/laser/RectangularPulse.java b/src/main/java/pulse/problem/laser/RectangularPulse.java index 2ba6b9d..7bc5dfb 100644 --- a/src/main/java/pulse/problem/laser/RectangularPulse.java +++ b/src/main/java/pulse/problem/laser/RectangularPulse.java @@ -14,11 +14,12 @@ */ public class RectangularPulse extends PulseTemporalShape { - private final static int MIN_POINTS = 4; - + private static final long serialVersionUID = 8207478409316696745L; + private final static int MIN_POINTS = 2; + /** * @param time the time measured from the start of the laser pulse. - * @return + * @return */ @Override public double evaluateAt(double time) { @@ -35,7 +36,7 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { public PulseTemporalShape copy() { return new RectangularPulse(); } - + @Override public int getRequiredDiscretisation() { return MIN_POINTS; diff --git a/src/main/java/pulse/problem/laser/TrapezoidalPulse.java b/src/main/java/pulse/problem/laser/TrapezoidalPulse.java index c305b81..908e2cf 100644 --- a/src/main/java/pulse/problem/laser/TrapezoidalPulse.java +++ b/src/main/java/pulse/problem/laser/TrapezoidalPulse.java @@ -18,12 +18,13 @@ */ public class TrapezoidalPulse extends PulseTemporalShape { + private static final long serialVersionUID = 2089809680713225034L; private double rise; private double fall; private double h; - private final static int MIN_POINTS = 6; - + private final static int MIN_POINTS = 8; + /** * Constructs a trapezoidal pulse using a default segmentation principle. * The reader is referred to the {@code .xml} file containing the default @@ -42,7 +43,7 @@ public TrapezoidalPulse(TrapezoidalPulse another) { this.fall = another.fall; this.h = another.h; } - + /** * Calculates the height of the trapezium which under current segmentation * will yield an area of unity. @@ -125,10 +126,10 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { public PulseTemporalShape copy() { return new TrapezoidalPulse(this); } - + @Override public int getRequiredDiscretisation() { return MIN_POINTS; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/ADIScheme.java b/src/main/java/pulse/problem/schemes/ADIScheme.java index db0c7e4..b20b34d 100644 --- a/src/main/java/pulse/problem/schemes/ADIScheme.java +++ b/src/main/java/pulse/problem/schemes/ADIScheme.java @@ -14,6 +14,11 @@ */ public abstract class ADIScheme extends DifferenceScheme { + /** + * + */ + private static final long serialVersionUID = 4772650159522354367L; + /** * Creates a new {@code ADIScheme} with default values of grid density and * time factor. @@ -56,13 +61,14 @@ public ADIScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty public String toString() { return getString("ADIScheme.4"); } - + /** - * Contains only an empty statement, as the pulse needs to be calculated not only - * for the time step {@code m} but also accounting for the radial coordinate + * Contains only an empty statement, as the pulse needs to be calculated not + * only for the time step {@code m} but also accounting for the radial + * coordinate + * * @param m thte time step */ - @Override public void prepareStep(int m) { //do nothing diff --git a/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java b/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java index acaa5cd..450db9d 100644 --- a/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java +++ b/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java @@ -11,6 +11,7 @@ */ public class BlockMatrixAlgorithm extends TridiagonalMatrixAlgorithm { + private static final long serialVersionUID = -6553638438386098008L; private final double[] gamma; private final double[] p; private final double[] q; @@ -36,7 +37,7 @@ public void evaluateBeta(final double[] U) { super.evaluateBeta(U); var alpha = getAlpha(); var beta = getBeta(); - + final int N = getGridPoints(); p[N - 1] = beta[N]; @@ -51,7 +52,7 @@ public void evaluateBeta(final double[] U) { @Override public void evaluateBeta(final double[] U, final int start, final int endExclusive) { var alpha = getAlpha(); - + final double h = this.getGridStep(); final double HX2_TAU = h * h / this.getTimeStep(); diff --git a/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java b/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java index f4577d9..17976a2 100644 --- a/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java +++ b/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java @@ -14,6 +14,7 @@ public abstract class CoupledImplicitScheme extends ImplicitScheme { + private static final long serialVersionUID = 1974655675470727643L; private RadiativeTransferCoupling coupling; private RTECalculationStatus calculationStatus; private boolean autoUpdateFluxes = true; //should be false for nonlinear solvers @@ -29,11 +30,11 @@ public CoupledImplicitScheme(NumericProperty N, NumericProperty timeFactor, Nume this(N, timeFactor); setTimeLimit(timeLimit); } - + @Override public void finaliseStep() throws SolverException { super.finaliseStep(); - if(autoUpdateFluxes) { + if (autoUpdateFluxes) { var rte = this.getCoupling().getRadiativeTransferEquation(); setCalculationStatus(rte.compute(getCurrentSolution())); } @@ -59,11 +60,11 @@ public final RTECalculationStatus getCalculationStatus() { public final void setCalculationStatus(RTECalculationStatus calculationStatus) throws SolverException { this.calculationStatus = calculationStatus; if (calculationStatus != RTECalculationStatus.NORMAL) { - throw new SolverException(calculationStatus.toString(), + throw new SolverException(calculationStatus.toString(), RTE_SOLVER_ERROR); } } - + public final RadiativeTransferCoupling getCoupling() { return coupling; } @@ -72,18 +73,18 @@ public final void setCoupling(RadiativeTransferCoupling coupling) { this.coupling = coupling; this.coupling.setParent(this); } - + public final boolean isAutoUpdateFluxes() { return this.autoUpdateFluxes; } - + public final void setAutoUpdateFluxes(boolean auto) { this.autoUpdateFluxes = auto; } - + @Override public Class[] domain() { return new Class[]{ParticipatingMedium.class}; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/DifferenceScheme.java b/src/main/java/pulse/problem/schemes/DifferenceScheme.java index 59c9220..64de3f8 100644 --- a/src/main/java/pulse/problem/schemes/DifferenceScheme.java +++ b/src/main/java/pulse/problem/schemes/DifferenceScheme.java @@ -1,5 +1,6 @@ package pulse.problem.schemes; +import java.util.Objects; import static pulse.properties.NumericProperties.def; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericProperty.requireType; @@ -28,7 +29,7 @@ */ public abstract class DifferenceScheme extends PropertyHolder implements Reflexive { - private DiscretePulse discretePulse; + private transient DiscretePulse discretePulse; private Grid grid; private double timeLimit; @@ -91,8 +92,8 @@ public void copyFrom(DifferenceScheme df) { protected void prepare(Problem problem) throws SolverException { if (discretePulse == null) { discretePulse = problem.discretePulseOn(grid); - } - discretePulse.recalculate(); + } + discretePulse.init(); clearArrays(); } @@ -113,14 +114,14 @@ public void runTimeSequence(Problem problem, final double offset, final double e int numPoints = (int) curve.getNumPoints().getValue(); - final double startTime = (double) curve.getTimeShift().getValue(); - final double timeSegment = (endTime - startTime - offset) / problem.getProperties().timeFactor(); + final double startTime = (double) curve.getTimeShift().getValue(); + final double timeSegment = (endTime - startTime - offset) / problem.getProperties().characteristicTime(); double tau = grid.getTimeStep(); final double dt = timeSegment / (numPoints - 1); - timeInterval = Math.max( (int) (dt / tau), 1); + timeInterval = Math.max((int) (dt / tau), 1); - double wFactor = timeInterval * tau * problem.getProperties().timeFactor(); + double wFactor = timeInterval * tau * problem.getProperties().characteristicTime(); // First point (index = 0) is always (0.0, 0.0) curve.addPoint(0.0, 0.0); @@ -296,16 +297,16 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { setTimeLimit(property); } } - + public abstract double signal(); - + public abstract void clearArrays(); public abstract void timeStep(int m) throws SolverException; public abstract void finaliseStep() throws SolverException; - /** + /** * Retrieves all problem statements that can be solved with this * implementation of the difference scheme. * @@ -321,5 +322,5 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { * @return an exact copy of this {@code DifferenceScheme}. */ public abstract DifferenceScheme copy(); - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/problem/schemes/DistributedDetection.java b/src/main/java/pulse/problem/schemes/DistributedDetection.java index 88831e4..3d17946 100644 --- a/src/main/java/pulse/problem/schemes/DistributedDetection.java +++ b/src/main/java/pulse/problem/schemes/DistributedDetection.java @@ -1,5 +1,6 @@ package pulse.problem.schemes; +import java.io.Serializable; import java.util.stream.IntStream; import pulse.problem.statements.model.AbsorptionModel; @@ -11,7 +12,9 @@ * {@code AbsorptionModel}. * */ -public class DistributedDetection { +public class DistributedDetection implements Serializable { + + private static final long serialVersionUID = 3587781877001360511L; /** * Calculates the effective signal registered by the detector, which takes @@ -35,4 +38,4 @@ public static double evaluateSignal(final AbsorptionModel absorption, final Grid return signal * 0.5 * hx; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/ExplicitScheme.java b/src/main/java/pulse/problem/schemes/ExplicitScheme.java index 70a47ba..e4b858c 100644 --- a/src/main/java/pulse/problem/schemes/ExplicitScheme.java +++ b/src/main/java/pulse/problem/schemes/ExplicitScheme.java @@ -19,6 +19,11 @@ */ public abstract class ExplicitScheme extends OneDimensionalScheme { + /** + * + */ + private static final long serialVersionUID = -3025913683505686334L; + /** * Constructs a default explicit scheme using the default values of * {@code GRID_DENSITY} and {@code TAU_FACTOR}. diff --git a/src/main/java/pulse/problem/schemes/FixedPointIterations.java b/src/main/java/pulse/problem/schemes/FixedPointIterations.java index 1c359a4..dbf5992 100644 --- a/src/main/java/pulse/problem/schemes/FixedPointIterations.java +++ b/src/main/java/pulse/problem/schemes/FixedPointIterations.java @@ -1,6 +1,8 @@ package pulse.problem.schemes; import static java.lang.Math.abs; + +import java.io.Serializable; import java.util.Arrays; import pulse.problem.schemes.solvers.SolverException; import static pulse.problem.schemes.solvers.SolverException.SolverExceptionType.FINITE_DIFFERENCE_ERROR; @@ -10,7 +12,7 @@ * page * */ -public interface FixedPointIterations { +public interface FixedPointIterations extends Serializable { /** * Performs iterations until the convergence criterion is satisfied.The @@ -30,9 +32,9 @@ public default void doIterations(double[] V, final double error, final int m) th final int N = V.length - 1; - for (double V_0 = error + 1, V_N = error + 1; - abs(V[0] - V_0)/abs(V[0] + V_0 + 1e-16) > error - || abs(V[N] - V_N)/abs(V[N] + V_N + 1e-16) > error; finaliseIteration(V)) { + for (double V_0 = error + 1, V_N = error + 1; + abs(V[0] - V_0) / abs(V[0] + V_0 + 1e-16) > error + || abs(V[N] - V_N) / abs(V[N] + V_N + 1e-16) > error; finaliseIteration(V)) { V_N = V[N]; V_0 = V[0]; diff --git a/src/main/java/pulse/problem/schemes/Grid.java b/src/main/java/pulse/problem/schemes/Grid.java index f68467b..d8a0b33 100644 --- a/src/main/java/pulse/problem/schemes/Grid.java +++ b/src/main/java/pulse/problem/schemes/Grid.java @@ -2,7 +2,6 @@ import static java.lang.Math.pow; import static java.lang.Math.rint; -import static java.lang.String.format; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericProperty.requireType; import static pulse.properties.NumericPropertyKeyword.GRID_DENSITY; @@ -10,7 +9,6 @@ import java.util.Set; -import pulse.problem.laser.DiscretePulse; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; import pulse.util.PropertyHolder; @@ -27,6 +25,7 @@ */ public class Grid extends PropertyHolder { + private static final long serialVersionUID = -4293010190416468656L; private double hx; private double tau; private double tauFactor; @@ -46,7 +45,7 @@ public Grid(NumericProperty gridDensity, NumericProperty timeFactor) { this.N = (int) gridDensity.getValue(); this.tauFactor = (double) timeFactor.getValue(); hx = 1. / N; - setTimeStep(tauFactor * pow(hx, 2)); + setTimeStep(tauFactor * pow(hx, 2)); } protected Grid() { @@ -63,37 +62,6 @@ public Grid copy() { return new Grid(getGridDensity(), getTimeFactor()); } - /** - * Optimises the {@code Grid} parameters so that the timestep is - * sufficiently small to enable accurate pulse correction. - *

- * This can change the {@code tauFactor} and {@code tau} variables in the - * {@code Grid} object if {@code discretePulseWidth/(M - 1) < grid.tau}, - * where M is the required number of pulse calculations. - *

- * - * @param pulse the discrete pulse representation - * @see PulseTemporalShape.getRequiredDiscretisation() - */ - public final void adjustTimeStep(DiscretePulse pulse) { - double timeFactor = pulse.getConversionFactor(); - - final int reqPoints = pulse.getPulse().getPulseShape().getRequiredDiscretisation(); - - double pNominalWidth = (double) pulse.getPulse().getPulseWidth().getValue(); - double pResolvedWidth = pulse.resolvedPulseWidth(); - double pWidth = pNominalWidth < pResolvedWidth ? pResolvedWidth : pNominalWidth; - - double newTau = pWidth / timeFactor / (reqPoints > 1 ? reqPoints - 1 : 1); - double newTauFactor = newTau / (hx * hx); - - final double EPS = 1E-10; - if (newTauFactor < tauFactor - EPS) { - setTimeFactor(derive(TAU_FACTOR, newTauFactor)); - } - - } - /** * The listed properties include {@code GRID_DENSITY} and * {@code TAU_FACTOR}. @@ -151,7 +119,7 @@ public final double getTimeStep() { protected final void setTimeStep(double tau) { this.tau = tau; - + } /** @@ -196,7 +164,7 @@ public void setGridDensity(NumericProperty gridDensity) { requireType(gridDensity, GRID_DENSITY); this.N = (int) gridDensity.getValue(); hx = 1. / N; - setTimeStep(tauFactor * pow(hx, 2)); + setTimeStep(tauFactor * pow(hx, 2)); firePropertyChanged(this, gridDensity); } @@ -209,7 +177,7 @@ public void setGridDensity(NumericProperty gridDensity) { public void setTimeFactor(NumericProperty timeFactor) { requireType(timeFactor, TAU_FACTOR); this.tauFactor = (double) timeFactor.getValue(); - setTimeStep(tauFactor * pow(hx, 2)); + setTimeStep(tauFactor * pow(hx, 2)); firePropertyChanged(this, timeFactor); } @@ -223,7 +191,7 @@ public void setTimeFactor(NumericProperty timeFactor) { * @return a double representing the time on the finite grid */ public final double gridTime(double time, double dimensionFactor) { - return rint((time / dimensionFactor) / tau) * tau; + return ((int) (time / dimensionFactor / tau)) * tau; } /** @@ -241,15 +209,9 @@ public final double gridAxialDistance(double distance, double lengthFactor) { @Override public String toString() { - var sb = new StringBuilder(); - sb.append(""). - append(getClass().getSimpleName()) - .append(": hx=") - .append(format("%3.2e", hx)) - .append("; "). - append("τ=") - .append(format("%3.2e", tau)) - .append("; "); + var sb = new StringBuilder("Grid"); + sb.append(String.format("%n %-25s", this.getGridDensity())); + sb.append(String.format("%n %-25s", this.getTimeFactor())); return sb.toString(); } diff --git a/src/main/java/pulse/problem/schemes/Grid2D.java b/src/main/java/pulse/problem/schemes/Grid2D.java index 7c34840..35dac53 100644 --- a/src/main/java/pulse/problem/schemes/Grid2D.java +++ b/src/main/java/pulse/problem/schemes/Grid2D.java @@ -2,7 +2,6 @@ import static java.lang.Math.pow; import static java.lang.Math.rint; -import static java.lang.String.format; import pulse.problem.laser.DiscretePulse; import pulse.problem.laser.DiscretePulse2D; @@ -19,6 +18,7 @@ */ public class Grid2D extends Grid { + private static final long serialVersionUID = 564113358979595637L; private double hy; protected Grid2D() { @@ -56,19 +56,17 @@ public void setTimeFactor(NumericProperty timeFactor) { * * @param pulse the discrete puls representation */ - public void adjustStepSize(DiscretePulse pulse) { - var pulse2d = (DiscretePulse2D)pulse; + var pulse2d = (DiscretePulse2D) pulse; double pulseSpotSize = pulse2d.getDiscretePulseSpot(); - if(hy > pulseSpotSize) { + if (hy > pulseSpotSize) { final int INCREMENT = 5; final int newN = getGridDensityValue() + INCREMENT; setGridDensityValue(newN); adjustStepSize(pulse); } - - adjustTimeStep(pulse); + } @Override @@ -100,13 +98,8 @@ public double gridRadialDistance(double radial, double lengthFactor) { return rint((radial / lengthFactor) / hy) * hy; } - @Override - public String toString() { - return super.toString() + "hy=" + format("%3.3f", hy); - } - public double getYStep() { return hy; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/ImplicitScheme.java b/src/main/java/pulse/problem/schemes/ImplicitScheme.java index 217cb28..0d72003 100644 --- a/src/main/java/pulse/problem/schemes/ImplicitScheme.java +++ b/src/main/java/pulse/problem/schemes/ImplicitScheme.java @@ -18,6 +18,10 @@ */ public abstract class ImplicitScheme extends OneDimensionalScheme { + /** + * + */ + private static final long serialVersionUID = 2785615380656900783L; private TridiagonalMatrixAlgorithm tridiagonal; /** @@ -67,14 +71,15 @@ protected void prepare(Problem problem) throws SolverException { } /** - * Calculates the solution at the boundaries using the boundary conditions - * specific to the problem statement and runs the tridiagonal matrix algorithm - * to evaluate solution at the intermediate grid points. + * Calculates the solution at the boundaries using the boundary conditions + * specific to the problem statement and runs the tridiagonal matrix + * algorithm to evaluate solution at the intermediate grid points. + * * @param m the time step - * @throws SolverException if the calculation failed - * @see leftBoundary(), evalRightBoundary(), pulse.problem.schemes.TridiagonalMatrixAlgorithm.sweep() + * @throws SolverException if the calculation failed + * @see leftBoundary(), evalRightBoundary(), + * pulse.problem.schemes.TridiagonalMatrixAlgorithm.sweep() */ - @Override public void timeStep(final int m) throws SolverException { leftBoundary(m); @@ -111,4 +116,4 @@ public void setTridiagonalMatrixAlgorithm(TridiagonalMatrixAlgorithm tridiagonal this.tridiagonal = tridiagonal; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/MixedScheme.java b/src/main/java/pulse/problem/schemes/MixedScheme.java index a02bfe3..fa53e69 100644 --- a/src/main/java/pulse/problem/schemes/MixedScheme.java +++ b/src/main/java/pulse/problem/schemes/MixedScheme.java @@ -17,6 +17,11 @@ */ public abstract class MixedScheme extends ImplicitScheme { + /** + * + */ + private static final long serialVersionUID = -770528855578192638L; + /** * Constructs a default semi-implicit scheme using the default values of * {@code GRID_DENSITY} and {@code TAU_FACTOR}. diff --git a/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java b/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java index 5483b5a..b58bf0f 100644 --- a/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java +++ b/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java @@ -15,8 +15,7 @@ protected OneDimensionalScheme() { protected OneDimensionalScheme(NumericProperty timeLimit) { super(timeLimit); } - - + @Override public void clearArrays() { final int N = (int) getGrid().getGridDensity().getValue(); @@ -32,9 +31,9 @@ public double signal() { /** * Overwrites previously calculated temperature values with the calculations * made at the current time step + * * @throws SolverException if the calculation failed */ - @Override public void finaliseStep() throws SolverException { System.arraycopy(V, 0, U, 0, V.length); diff --git a/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java b/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java index 189548a..e507e00 100644 --- a/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java +++ b/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java @@ -4,7 +4,6 @@ import pulse.problem.schemes.rte.RadiativeTransferSolver; import pulse.problem.schemes.rte.dom.DiscreteOrdinatesMethod; -import pulse.problem.statements.ClassicalProblem; import pulse.problem.statements.ParticipatingMedium; import pulse.problem.statements.Problem; import pulse.problem.statements.model.ThermoOpticalProperties; @@ -16,9 +15,11 @@ public class RadiativeTransferCoupling extends PropertyHolder { + private static final long serialVersionUID = -8969606772435213260L; private RadiativeTransferSolver rte; - private InstanceDescriptor instanceDescriptor = new InstanceDescriptor( - "RTE Solver Selector", RadiativeTransferSolver.class); + private InstanceDescriptor instanceDescriptor + = new InstanceDescriptor( + "RTE Solver Selector", RadiativeTransferSolver.class); public RadiativeTransferCoupling() { instanceDescriptor.setSelectedDescriptor(DiscreteOrdinatesMethod.class.getSimpleName()); @@ -34,7 +35,7 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { public void init(ParticipatingMedium problem, Grid grid) { if (rte == null) { - + if (!(problem.getProperties() instanceof ThermoOpticalProperties)) { throw new IllegalArgumentException("Illegal problem type: " + problem); } diff --git a/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java b/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java index 9d98c30..0b76d96 100644 --- a/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java +++ b/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java @@ -1,13 +1,17 @@ package pulse.problem.schemes; +import java.io.Serializable; + /** * Implements the tridiagonal matrix algorithm (Thomas algorithms) for solving * systems of linear equations. Applicable to such systems where the forming - * matrix has a tridiagonal form: Ai*xi-1 - Bi xi + Ci xi+1 = -Fi. + * matrix has a tridiagonal form: Ai*xi-1 - Bi + * xi + Ci xi+1 = -Fi. * */ -public class TridiagonalMatrixAlgorithm { +public class TridiagonalMatrixAlgorithm implements Serializable { + private static final long serialVersionUID = 8201903787985856087L; private final double tau; private final double h; @@ -21,10 +25,10 @@ public class TridiagonalMatrixAlgorithm { public TridiagonalMatrixAlgorithm(Grid grid) { tau = grid.getTimeStep(); - N = grid.getGridDensityValue(); - h = grid.getXStep(); - alpha = new double[N + 2]; - beta = new double[N + 2]; + N = grid.getGridDensityValue(); + h = grid.getXStep(); + alpha = new double[N + 2]; + beta = new double[N + 2]; } /** @@ -59,6 +63,7 @@ public void evaluateBeta(final double[] U) { /** * Calculates the {@code beta} coefficients as part of the tridiagonal * matrix algorithm. + * * @param U * @param start * @param endExclusive @@ -116,17 +121,17 @@ protected double getCoefB() { protected double getCoefC() { return c; } - + public final double getTimeStep() { return tau; } - + public final int getGridPoints() { return N; } - + public final double getGridStep() { return h; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java b/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java index f395d00..5f65f01 100644 --- a/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java +++ b/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java @@ -1,11 +1,15 @@ package pulse.problem.schemes.rte; - +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import org.apache.commons.math3.analysis.UnivariateFunction; +import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction; import static pulse.math.MathUtils.fastPowLoop; import pulse.problem.statements.NonlinearProblem; import pulse.problem.statements.Pulse2D; - +import pulse.util.FunctionSerializer; /** * Contains methods for calculating the integral spectral characteristics of a @@ -14,10 +18,11 @@ * {@code SplineInterpolator}. * */ -public class BlackbodySpectrum { +public class BlackbodySpectrum implements Serializable { - private UnivariateFunction interpolation; - private final double reductionFactor; + private static final long serialVersionUID = 4628793608666198231L; + private transient UnivariateFunction interpolation; + private double reductionFactor; /** * Creates a {@code BlackbodySpectrum}. Calculates the reduction factor @@ -36,16 +41,16 @@ public BlackbodySpectrum(NonlinearProblem p) { public String toString() { return "[" + getClass().getSimpleName() + ": Rel. heating = " + reductionFactor + "]"; } - + /** * Calculates the emissive power. This is equal to * 0.25 T0Tm [1 * +δTm /T0 θ (x) * ]4, where θ is the reduced temperature. + * * @param reducedTemperature the dimensionless reduced temperature * @return the amount of emissive power */ - public double emissivePower(double reducedTemperature) { return 0.25 / reductionFactor * fastPowLoop(1.0 + reducedTemperature * reductionFactor, 4); } @@ -63,7 +68,7 @@ public double radianceAt(double x) { } /** - * Calculates the emissive power at the given coordinate. + * Calculates the emissive power at the given coordinate. * * @param x the geometric coordinate inside the sample * @return the local emissive power value @@ -90,4 +95,22 @@ public final double radiance(double reducedTemperature) { return emissivePower(reducedTemperature) / Math.PI; } + /* + * Serialization + */ + private void writeObject(ObjectOutputStream oos) + throws IOException { + // default serialization + oos.defaultWriteObject(); + // write the object + FunctionSerializer.writeSplineFunction((PolynomialSplineFunction) interpolation, oos); + } + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + // default deserialization + ois.defaultReadObject(); + this.interpolation = FunctionSerializer.readSplineFunction(ois); + } + } \ No newline at end of file diff --git a/src/main/java/pulse/problem/schemes/rte/DerivativeCalculator.java b/src/main/java/pulse/problem/schemes/rte/DerivativeCalculator.java index 8ad2b58..23a128f 100644 --- a/src/main/java/pulse/problem/schemes/rte/DerivativeCalculator.java +++ b/src/main/java/pulse/problem/schemes/rte/DerivativeCalculator.java @@ -1,11 +1,13 @@ package pulse.problem.schemes.rte; +import java.io.Serializable; + /** * This is basically a coupling interface between a {@code Solver} and a * {@code RadiativeTransferSolver}. * */ -public interface DerivativeCalculator { +public interface DerivativeCalculator extends Serializable { /** * Calculates the average value of the flux derivatives at the diff --git a/src/main/java/pulse/problem/schemes/rte/Fluxes.java b/src/main/java/pulse/problem/schemes/rte/Fluxes.java index 7c3bc78..be74c53 100644 --- a/src/main/java/pulse/problem/schemes/rte/Fluxes.java +++ b/src/main/java/pulse/problem/schemes/rte/Fluxes.java @@ -23,13 +23,13 @@ public Fluxes(NumericProperty gridDensity, NumericProperty opticalThickness) { public void store() { System.arraycopy(fluxes, 0, storedFluxes, 0, N + 1); // store previous results } - + /** - * Checks whether all stored values are finite. This is equivalent to summing - * all elements and checking whether the sum if finite. + * Checks whether all stored values are finite. This is equivalent to + * summing all elements and checking whether the sum if finite. + * * @return {@code true} if the elements are finite. */ - public RTECalculationStatus checkArrays() { double sum = Arrays.stream(fluxes).sum() + Arrays.stream(storedFluxes).sum(); return Double.isFinite(sum) ? NORMAL : INVALID_FLUXES; diff --git a/src/main/java/pulse/problem/schemes/rte/FluxesAndExplicitDerivatives.java b/src/main/java/pulse/problem/schemes/rte/FluxesAndExplicitDerivatives.java index 2b4ac0f..f569717 100644 --- a/src/main/java/pulse/problem/schemes/rte/FluxesAndExplicitDerivatives.java +++ b/src/main/java/pulse/problem/schemes/rte/FluxesAndExplicitDerivatives.java @@ -1,19 +1,17 @@ package pulse.problem.schemes.rte; -import java.util.Arrays; -import static pulse.problem.schemes.rte.RTECalculationStatus.INVALID_FLUXES; -import static pulse.problem.schemes.rte.RTECalculationStatus.NORMAL; import pulse.properties.NumericProperty; public class FluxesAndExplicitDerivatives extends Fluxes { + private static final long serialVersionUID = -6308711091434946173L; private double fd[]; private double fdStored[]; public FluxesAndExplicitDerivatives(NumericProperty gridDensity, NumericProperty opticalThickness) { super(gridDensity, opticalThickness); } - + @Override public void init() { super.init(); diff --git a/src/main/java/pulse/problem/schemes/rte/FluxesAndImplicitDerivatives.java b/src/main/java/pulse/problem/schemes/rte/FluxesAndImplicitDerivatives.java index b2fd4e3..038bc2e 100644 --- a/src/main/java/pulse/problem/schemes/rte/FluxesAndImplicitDerivatives.java +++ b/src/main/java/pulse/problem/schemes/rte/FluxesAndImplicitDerivatives.java @@ -4,6 +4,8 @@ public class FluxesAndImplicitDerivatives extends Fluxes { + private static final long serialVersionUID = -4161296401342482405L; + public FluxesAndImplicitDerivatives(NumericProperty gridDensity, NumericProperty opticalThickness) { super(gridDensity, opticalThickness); } diff --git a/src/main/java/pulse/problem/schemes/rte/RTECalculationListener.java b/src/main/java/pulse/problem/schemes/rte/RTECalculationListener.java index 71da5f9..f761f7c 100644 --- a/src/main/java/pulse/problem/schemes/rte/RTECalculationListener.java +++ b/src/main/java/pulse/problem/schemes/rte/RTECalculationListener.java @@ -1,11 +1,13 @@ package pulse.problem.schemes.rte; +import java.io.Serializable; + /** * Used to listed to status updates in {@code RadiativeTransferSolver} * subclasses. * */ -public interface RTECalculationListener { +public interface RTECalculationListener extends Serializable { /** * Invoked when a sub-step of the RTE solution has finished. diff --git a/src/main/java/pulse/problem/schemes/rte/RTECalculationStatus.java b/src/main/java/pulse/problem/schemes/rte/RTECalculationStatus.java index f579004..83054ca 100644 --- a/src/main/java/pulse/problem/schemes/rte/RTECalculationStatus.java +++ b/src/main/java/pulse/problem/schemes/rte/RTECalculationStatus.java @@ -22,11 +22,9 @@ public enum RTECalculationStatus { * The grid density required to reach the error threshold was too large. */ GRID_TOO_LARGE, - /** * The radiative fluxes contain illegal values. */ - INVALID_FLUXES; - + } diff --git a/src/main/java/pulse/problem/schemes/rte/dom/ButcherTableau.java b/src/main/java/pulse/problem/schemes/rte/dom/ButcherTableau.java index 9c557e3..5d86e8f 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/ButcherTableau.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/ButcherTableau.java @@ -1,5 +1,6 @@ package pulse.problem.schemes.rte.dom; +import java.io.Serializable; import pulse.math.linear.Matrices; import pulse.math.linear.SquareMatrix; import pulse.math.linear.Vector; @@ -10,8 +11,9 @@ * Variable names correspond to the standard notations. * */ -public class ButcherTableau implements Descriptive { +public class ButcherTableau implements Descriptive, Serializable { + private static final long serialVersionUID = -8856270519744473886L; private Vector b; private Vector bHat; private Vector c; diff --git a/src/main/java/pulse/problem/schemes/rte/dom/CompositeGaussianQuadrature.java b/src/main/java/pulse/problem/schemes/rte/dom/CompositeGaussianQuadrature.java index a242ef4..4e3de93 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/CompositeGaussianQuadrature.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/CompositeGaussianQuadrature.java @@ -1,5 +1,6 @@ package pulse.problem.schemes.rte.dom; +import java.io.Serializable; import pulse.math.LegendrePoly; import pulse.math.MathUtils; @@ -10,7 +11,9 @@ * @author Teymur Aliev, Vadim Zborovskii, Artem Lunev * */ -public class CompositeGaussianQuadrature { +public class CompositeGaussianQuadrature implements Serializable { + + private static final long serialVersionUID = 780827333372523309L; private LegendrePoly poly; diff --git a/src/main/java/pulse/problem/schemes/rte/dom/CornetteSchanksPF.java b/src/main/java/pulse/problem/schemes/rte/dom/CornetteSchanksPF.java index f2a695e..5dfccb4 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/CornetteSchanksPF.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/CornetteSchanksPF.java @@ -6,14 +6,16 @@ import pulse.problem.statements.model.ThermoOpticalProperties; /** - * The single-parameter Cornette-Schanks scattering phase function. - * It converges to the Rayleigh phase function as 〈μ〉 → 0 and approaches - * the Henyey–Greenstein phase function as |〈μ〉| → 1 + * The single-parameter Cornette-Schanks scattering phase function. It converges + * to the Rayleigh phase function as 〈μ〉 → 0 and approaches the + * Henyey–Greenstein phase function as |〈μ〉| → 1 + * * @see https://doi.org/10.1364/ao.31.003152 * */ public class CornetteSchanksPF extends PhaseFunction { + private static final long serialVersionUID = -4371291780762389604L; private double anisoFactor; private double onePlusGSq; private double g2; @@ -29,14 +31,14 @@ public void init(ThermoOpticalProperties top) { g2 = 2.0 * anisotropy; final double aSq = anisotropy * anisotropy; onePlusGSq = 1.0 + aSq; - anisoFactor = 1.5*(1.0 - aSq)/(2.0 + aSq); + anisoFactor = 1.5 * (1.0 - aSq) / (2.0 + aSq); } @Override public double function(final int i, final int k) { - double cosine = cosineTheta(i,k); + double cosine = cosineTheta(i, k); final double f = onePlusGSq - g2 * cosine; - return anisoFactor * (1.0 + cosine*cosine) / (f * sqrt(f)); + return anisoFactor * (1.0 + cosine * cosine) / (f * sqrt(f)); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/rte/dom/DiscreteOrdinatesMethod.java b/src/main/java/pulse/problem/schemes/rte/dom/DiscreteOrdinatesMethod.java index af59449..0e3e5d4 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/DiscreteOrdinatesMethod.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/DiscreteOrdinatesMethod.java @@ -23,6 +23,7 @@ */ public class DiscreteOrdinatesMethod extends RadiativeTransferSolver { + private static final long serialVersionUID = 2881363894773388976L; private InstanceDescriptor integratorDescriptor = new InstanceDescriptor( "Integrator selector", AdaptiveIntegrator.class); private InstanceDescriptor iterativeSolverSelector = new InstanceDescriptor( @@ -55,7 +56,6 @@ public DiscreteOrdinatesMethod(ParticipatingMedium problem, Grid grid) { setIterativeSolver(iterativeSolverSelector.newInstance(IterativeSolver.class)); phaseFunctionSelector.setSelectedDescriptor(HenyeyGreensteinPF.class.getSimpleName()); - phaseFunctionSelector.addListener(() -> initPhaseFunction(properties, discrete)); initPhaseFunction(properties, discrete); init(problem, grid); @@ -66,7 +66,8 @@ public DiscreteOrdinatesMethod(ParticipatingMedium problem, Grid grid) { iterativeSolverSelector .addListener(() -> setIterativeSolver(iterativeSolverSelector.newInstance(IterativeSolver.class))); - + + phaseFunctionSelector.addListener(() -> initPhaseFunction(properties, discrete)); } @Override @@ -78,7 +79,7 @@ public RTECalculationStatus compute(double[] tempArray) { if (status == RTECalculationStatus.NORMAL) { fluxesAndDerivatives(tempArray.length); } - + fireStatusUpdate(status); return status; } @@ -94,10 +95,10 @@ private void fluxesAndDerivatives(final int nExclusive) { for (int i = 0; i < nExclusive; i++) { double flux = DOUBLE_PI * discrete.firstMoment(interpolation[0], i); fluxes.setFlux(i, flux); - fluxes.setFluxDerivative(i, + fluxes.setFluxDerivative(i, -DOUBLE_PI * discrete.firstMoment(interpolation[1], i)); } - + } @Override @@ -108,8 +109,8 @@ public String getDescriptor() { @Override public void init(ParticipatingMedium problem, Grid grid) { super.init(problem, grid); - var top = (ThermoOpticalProperties)problem.getProperties(); - initPhaseFunction(top, + var top = (ThermoOpticalProperties) problem.getProperties(); + initPhaseFunction(top, integrator.getDiscretisation()); integrator.init(problem); integrator.getPhaseFunction().init(top); diff --git a/src/main/java/pulse/problem/schemes/rte/dom/DiscreteQuantities.java b/src/main/java/pulse/problem/schemes/rte/dom/DiscreteQuantities.java index 846dc1b..e81389a 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/DiscreteQuantities.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/DiscreteQuantities.java @@ -1,12 +1,15 @@ package pulse.problem.schemes.rte.dom; +import java.io.Serializable; + /** * Defines the main quantities calculated within the discrete ordinates method. * This includes the various intensity and flux arrays used internally by the * integrators. */ -public class DiscreteQuantities { +public class DiscreteQuantities implements Serializable { + private static final long serialVersionUID = -3997479317699236996L; private double[][] I; private double[][] Ik; private double[][] f; diff --git a/src/main/java/pulse/problem/schemes/rte/dom/Discretisation.java b/src/main/java/pulse/problem/schemes/rte/dom/Discretisation.java index 932d7d6..91be63d 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/Discretisation.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/Discretisation.java @@ -23,6 +23,7 @@ */ public class Discretisation extends PropertyHolder { + private static final long serialVersionUID = 7069987686586911101L; private DiscreteQuantities quantities; private StretchedGrid grid; private OrdinateSet ordinates; diff --git a/src/main/java/pulse/problem/schemes/rte/dom/ExplicitRungeKutta.java b/src/main/java/pulse/problem/schemes/rte/dom/ExplicitRungeKutta.java index 648096e..42b98b9 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/ExplicitRungeKutta.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/ExplicitRungeKutta.java @@ -16,6 +16,7 @@ */ public class ExplicitRungeKutta extends AdaptiveIntegrator { + private static final long serialVersionUID = -2478658861611086402L; private ButcherTableau tableau; private DiscreteSelector tableauSelector; diff --git a/src/main/java/pulse/problem/schemes/rte/dom/FixedIterations.java b/src/main/java/pulse/problem/schemes/rte/dom/FixedIterations.java index 08cea6d..2a9ea83 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/FixedIterations.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/FixedIterations.java @@ -6,6 +6,8 @@ public class FixedIterations extends IterativeSolver { + private static final long serialVersionUID = -7308041206602757928L; + @Override public RTECalculationStatus doIterations(AdaptiveIntegrator integrator) { diff --git a/src/main/java/pulse/problem/schemes/rte/dom/HenyeyGreensteinPF.java b/src/main/java/pulse/problem/schemes/rte/dom/HenyeyGreensteinPF.java index f1e0d61..12af970 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/HenyeyGreensteinPF.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/HenyeyGreensteinPF.java @@ -2,7 +2,6 @@ import static java.lang.Math.sqrt; -import pulse.problem.statements.ParticipatingMedium; import pulse.problem.statements.model.ThermoOpticalProperties; /** @@ -11,6 +10,7 @@ */ public class HenyeyGreensteinPF extends PhaseFunction { + private static final long serialVersionUID = -2196189314681832809L; private double a1; private double a2; private double b1; diff --git a/src/main/java/pulse/problem/schemes/rte/dom/HermiteInterpolator.java b/src/main/java/pulse/problem/schemes/rte/dom/HermiteInterpolator.java index 1157850..93c4131 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/HermiteInterpolator.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/HermiteInterpolator.java @@ -1,5 +1,7 @@ package pulse.problem.schemes.rte.dom; +import java.io.Serializable; + /** * A globally C1 Hermite interpolator used to interpolate intensities * and derivatives in discrete ordinates method when solving the radiative @@ -8,8 +10,9 @@ * @author Vadim Zborovskii, Artem Lunev * */ -public class HermiteInterpolator { +public class HermiteInterpolator implements Serializable { + private static final long serialVersionUID = -1973954803574711053L; protected double y1; protected double y0; protected double d1; diff --git a/src/main/java/pulse/problem/schemes/rte/dom/LinearAnisotropicPF.java b/src/main/java/pulse/problem/schemes/rte/dom/LinearAnisotropicPF.java index 7c382c6..4693eeb 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/LinearAnisotropicPF.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/LinearAnisotropicPF.java @@ -9,6 +9,7 @@ */ public class LinearAnisotropicPF extends PhaseFunction { + private static final long serialVersionUID = 7074989018933263351L; private double g; public LinearAnisotropicPF(ThermoOpticalProperties top, Discretisation intensities) { @@ -29,7 +30,7 @@ public double partialSum(final int i, final int j, final int n1, final int n2Exc @Override public double function(final int i, final int k) { - return 1.0 + g * cosineTheta(i,k); + return 1.0 + g * cosineTheta(i, k); } } diff --git a/src/main/java/pulse/problem/schemes/rte/dom/ODEIntegrator.java b/src/main/java/pulse/problem/schemes/rte/dom/ODEIntegrator.java index 1785a2a..3dc388f 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/ODEIntegrator.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/ODEIntegrator.java @@ -21,9 +21,9 @@ public ODEIntegrator(Discretisation intensities) { protected void init(NonlinearProblem problem) { extract((ThermoOpticalProperties) problem.getProperties()); - setEmissionFunction( new BlackbodySpectrum(problem) ); + setEmissionFunction(new BlackbodySpectrum(problem)); } - + protected void extract(ThermoOpticalProperties properties) { discretisation.setEmissivity((double) properties.getEmissivity().getValue()); discretisation.setGrid(new StretchedGrid((double) properties.getOpticalThickness().getValue())); diff --git a/src/main/java/pulse/problem/schemes/rte/dom/OrdinateSet.java b/src/main/java/pulse/problem/schemes/rte/dom/OrdinateSet.java index 68662f6..fc09890 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/OrdinateSet.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/OrdinateSet.java @@ -1,5 +1,6 @@ package pulse.problem.schemes.rte.dom; +import java.io.Serializable; import static pulse.math.MathUtils.approximatelyEquals; import java.util.Arrays; @@ -11,8 +12,9 @@ * discretisation of a radiative transfer equation. * */ -public class OrdinateSet implements Descriptive { +public class OrdinateSet implements Descriptive, Serializable { + private static final long serialVersionUID = 4850346144315192409L; private double[] mu; private double[] w; diff --git a/src/main/java/pulse/problem/schemes/rte/dom/PhaseFunction.java b/src/main/java/pulse/problem/schemes/rte/dom/PhaseFunction.java index e8123f8..21374fb 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/PhaseFunction.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/PhaseFunction.java @@ -1,10 +1,10 @@ package pulse.problem.schemes.rte.dom; -import pulse.problem.statements.ParticipatingMedium; +import java.io.Serializable; import pulse.problem.statements.model.ThermoOpticalProperties; import pulse.util.Reflexive; -public abstract class PhaseFunction implements Reflexive { +public abstract class PhaseFunction implements Reflexive, Serializable { private final Discretisation intensities; private double anisotropy; @@ -14,15 +14,15 @@ public PhaseFunction(ThermoOpticalProperties top, Discretisation intensities) { this.intensities = intensities; init(top); } - - /** - * Calculates the cosine of the scattering angle as the product - * of the two discrete cosine nodes. + + /** + * Calculates the cosine of the scattering angle as the product of the two + * discrete cosine nodes. + * * @param i * @param k - * @return + * @return */ - public final double cosineTheta(int i, int k) { final var ordinates = getDiscreteIntensities().getOrdinates(); return ordinates.getNode(k) * ordinates.getNode(i); diff --git a/src/main/java/pulse/problem/schemes/rte/dom/StretchedGrid.java b/src/main/java/pulse/problem/schemes/rte/dom/StretchedGrid.java index 335d1ca..012eb5d 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/StretchedGrid.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/StretchedGrid.java @@ -5,18 +5,16 @@ import static pulse.properties.NumericPropertyKeyword.DOM_GRID_DENSITY; import static pulse.properties.NumericPropertyKeyword.GRID_STRETCHING_FACTOR; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; -import pulse.properties.Property; import pulse.util.PropertyHolder; public class StretchedGrid extends PropertyHolder { + private static final long serialVersionUID = -7987714138817824037L; + private double[] nodes; private double stretchingFactor; diff --git a/src/main/java/pulse/problem/schemes/rte/dom/SuccessiveOverrelaxation.java b/src/main/java/pulse/problem/schemes/rte/dom/SuccessiveOverrelaxation.java index a051ce9..1c44c06 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/SuccessiveOverrelaxation.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/SuccessiveOverrelaxation.java @@ -5,17 +5,15 @@ import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericPropertyKeyword.RELAXATION_PARAMETER; -import java.util.List; import java.util.Set; import pulse.problem.schemes.rte.RTECalculationStatus; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; -import pulse.properties.Property; public class SuccessiveOverrelaxation extends IterativeSolver { + private static final long serialVersionUID = 1135563981945852881L; private double W; public SuccessiveOverrelaxation() { diff --git a/src/main/java/pulse/problem/schemes/rte/dom/TRBDF2.java b/src/main/java/pulse/problem/schemes/rte/dom/TRBDF2.java index fa1bc4f..5ce2240 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/TRBDF2.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/TRBDF2.java @@ -14,6 +14,7 @@ */ public class TRBDF2 extends AdaptiveIntegrator { + private static final long serialVersionUID = 5488454845395333565L; /* * Coefficients of the Butcher tableau as originally defined in M.E. Hosea, L.E * Shampine/Applied Numerical Mathematics 20 (1996) 21-37 diff --git a/src/main/java/pulse/problem/schemes/rte/exact/ChandrasekharsQuadrature.java b/src/main/java/pulse/problem/schemes/rte/exact/ChandrasekharsQuadrature.java index 208ab29..535a603 100644 --- a/src/main/java/pulse/problem/schemes/rte/exact/ChandrasekharsQuadrature.java +++ b/src/main/java/pulse/problem/schemes/rte/exact/ChandrasekharsQuadrature.java @@ -11,9 +11,7 @@ import static pulse.properties.NumericProperty.requireType; import static pulse.properties.NumericPropertyKeyword.QUADRATURE_POINTS; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.Set; import java.util.stream.IntStream; @@ -25,8 +23,6 @@ import pulse.math.linear.Vector; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; -import pulse.properties.Property; /** * This quadrature methods of evaluating the composition product of the @@ -39,10 +35,11 @@ */ public class ChandrasekharsQuadrature extends CompositionProduct { + private static final long serialVersionUID = 3282258803373408111L; private int m; private double expLower; private double expUpper; - private LaguerreSolver solver; + private transient LaguerreSolver solver; private double[] moments; /** @@ -127,8 +124,8 @@ private SquareMatrix xMatrix(final double[] roots) { /** * Calculates \int_{r_{min}}^{r_{max}}{x^{l+1}exp(-x)dx}. * - * @param l an integer such that 0 <= l <= 2*m - 1. - * @return the value of this definite integral. + * @param l an integer such that 0 <= l <= 2*m - 1. @re + * turn the value of this definite integral. */ private static double auxilliaryIntegral(final double x, final int lPlusN, final double exp) { @@ -261,6 +258,9 @@ private double[] roots() { break; default: // use LaguerreSolver + if (solver == null) { + solver = new LaguerreSolver(); + } roots = Arrays.stream(solver.solveAllComplex(c, 1.0)).mapToDouble(complex -> complex.getReal()).toArray(); } diff --git a/src/main/java/pulse/problem/schemes/rte/exact/ExponentialIntegral.java b/src/main/java/pulse/problem/schemes/rte/exact/ExponentialIntegral.java index 340b5f2..b3833ac 100644 --- a/src/main/java/pulse/problem/schemes/rte/exact/ExponentialIntegral.java +++ b/src/main/java/pulse/problem/schemes/rte/exact/ExponentialIntegral.java @@ -16,6 +16,7 @@ */ class ExponentialIntegral extends MidpointIntegrator { + private static final long serialVersionUID = -5818633555456309668L; private double t; private int order; diff --git a/src/main/java/pulse/problem/schemes/rte/exact/NewtonCotesQuadrature.java b/src/main/java/pulse/problem/schemes/rte/exact/NewtonCotesQuadrature.java index f2f2328..546bb2c 100644 --- a/src/main/java/pulse/problem/schemes/rte/exact/NewtonCotesQuadrature.java +++ b/src/main/java/pulse/problem/schemes/rte/exact/NewtonCotesQuadrature.java @@ -2,13 +2,11 @@ import static java.lang.Math.max; import static java.lang.Math.min; -import static pulse.properties.NumericProperties.def; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericProperty.requireType; import static pulse.properties.NumericPropertyKeyword.INTEGRATION_CUTOFF; import static pulse.properties.NumericPropertyKeyword.INTEGRATION_SEGMENTS; -import java.util.List; import java.util.Set; import pulse.math.FixedIntervalIntegrator; @@ -16,8 +14,6 @@ import pulse.math.Segment; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; -import pulse.properties.Property; /** * A class for evaluating the composition product using a simple Newton-Cotes @@ -26,6 +22,7 @@ */ public class NewtonCotesQuadrature extends CompositionProduct { + private static final long serialVersionUID = -177127670003926420L; private final static int DEFAULT_SEGMENTS = 64; private final static double DEFAULT_CUTOFF = 16.0; private FixedIntervalIntegrator integrator; diff --git a/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringAnalyticalDerivatives.java b/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringAnalyticalDerivatives.java index bbcf0d3..dbc23a8 100644 --- a/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringAnalyticalDerivatives.java +++ b/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringAnalyticalDerivatives.java @@ -20,6 +20,7 @@ */ public class NonscatteringAnalyticalDerivatives extends NonscatteringRadiativeTransfer { + private static final long serialVersionUID = -7549047672012708753L; private static FunctionWithInterpolation ei2 = ExponentialIntegrals.get(2); public NonscatteringAnalyticalDerivatives(ParticipatingMedium problem, Grid grid) { @@ -31,8 +32,9 @@ public NonscatteringAnalyticalDerivatives(ParticipatingMedium problem, Grid grid /** * Evaluates fluxes and their derivatives using analytical formulae and the * selected numerical quadrature.Usually works best with the - {@code ChandrasekharsQuadrature} - * @return + * {@code ChandrasekharsQuadrature} + * + * @return */ @Override public RTECalculationStatus compute(double U[]) { diff --git a/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringDiscreteDerivatives.java b/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringDiscreteDerivatives.java index b620d91..c781488 100644 --- a/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringDiscreteDerivatives.java +++ b/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringDiscreteDerivatives.java @@ -14,6 +14,8 @@ */ public class NonscatteringDiscreteDerivatives extends NonscatteringRadiativeTransfer { + private static final long serialVersionUID = -6919734351838124553L; + public NonscatteringDiscreteDerivatives(ParticipatingMedium problem, Grid grid) { super(problem, grid); var properties = (ThermoOpticalProperties) problem.getProperties(); diff --git a/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringRadiativeTransfer.java b/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringRadiativeTransfer.java index a4583b5..3afd892 100644 --- a/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringRadiativeTransfer.java +++ b/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringRadiativeTransfer.java @@ -17,6 +17,8 @@ public abstract class NonscatteringRadiativeTransfer extends RadiativeTransferSolver { + private static final long serialVersionUID = 4934841542530728191L; + private static final FunctionWithInterpolation ei3 = ExponentialIntegrals.get(3); private double emissivity; @@ -27,9 +29,9 @@ public abstract class NonscatteringRadiativeTransfer extends RadiativeTransferSo private double radiosityFront; private double radiosityRear; - private InstanceDescriptor instanceDescriptor + private InstanceDescriptor instanceDescriptor = new InstanceDescriptor( - "Quadrature Selector", CompositionProduct.class); + "Quadrature Selector", CompositionProduct.class); protected NonscatteringRadiativeTransfer(ParticipatingMedium problem, Grid grid) { super(); @@ -50,6 +52,7 @@ public void init(ParticipatingMedium p, Grid grid) { * The superclass method will update the interpolation that the blackbody * spectrum uses to evaluate the temperature profile and calculate the * radiosities.A {@code NORMAL}status is always returned. + * * @param array */ @Override @@ -182,7 +185,7 @@ public double getRadiosityRear() { */ private void radiosities() { final double doubleReflectivity = 2.0 * (1.0 - emissivity); - + final double b = b(doubleReflectivity); final double sq = 1.0 - b * b; final double a1 = a1(doubleReflectivity); diff --git a/src/main/java/pulse/problem/schemes/solvers/ADILinearisedSolver.java b/src/main/java/pulse/problem/schemes/solvers/ADILinearisedSolver.java index d2472df..27196d5 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ADILinearisedSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ADILinearisedSolver.java @@ -17,6 +17,8 @@ */ public class ADILinearisedSolver extends ADIScheme implements Solver { + private static final long serialVersionUID = 5354981341912770336L; + private TridiagonalMatrixAlgorithm tridiagonal; private int N; @@ -77,12 +79,12 @@ public ADILinearisedSolver(NumericProperty N, NumericProperty timeFactor) { public ADILinearisedSolver(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) { super(N, timeFactor, timeLimit); } - + @Override public void clearArrays() { - N = (int) getGrid().getGridDensity().getValue(); - U1 = new double[N + 1][N + 1]; - U2 = new double[N + 1][N + 1]; + N = (int) getGrid().getGridDensity().getValue(); + U1 = new double[N + 1][N + 1]; + U2 = new double[N + 1][N + 1]; U1_E = new double[N + 3][N + 3]; U2_E = new double[N + 3][N + 3]; @@ -117,7 +119,6 @@ public void prepare(Problem problem) throws SolverException { l = (double) properties.getSampleThickness().getValue(); // end - // a[i]*u[i-1] - b[i]*u[i] + c[i]*u[i+1] = F[i] lastIndex = (int) (fovOuter / d / hx); lastIndex = lastIndex > N ? N : lastIndex; diff --git a/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolver.java b/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolver.java index 39e504c..5b706a4 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolver.java @@ -32,7 +32,7 @@ public abstract class ExplicitCoupledSolver extends ExplicitScheme private double zeta; private boolean autoUpdateFluxes = true; //should be false for nonlinear solvers - + public ExplicitCoupledSolver() { this(derive(GRID_DENSITY, 80), derive(TAU_FACTOR, 0.5)); } @@ -49,7 +49,7 @@ public void prepare(Problem problem) throws SolverException { var grid = getGrid(); - coupling.init((ParticipatingMedium)problem, grid); + coupling.init((ParticipatingMedium) problem, grid); fluxes = coupling.getRadiativeTransferEquation().getFluxes(); setCalculationStatus(fluxes.checkArrays()); @@ -69,7 +69,7 @@ public void prepare(Problem problem) throws SolverException { HX_NP = hx / Np; prefactor = tau * opticalThickness / Np; - zeta = (double) ((ParticipatingMedium)problem).getGeometricFactor().getValue(); + zeta = (double) ((ParticipatingMedium) problem).getGeometricFactor().getValue(); } @Override @@ -139,7 +139,7 @@ public final void setCoupling(RadiativeTransferCoupling coupling) { public String toString() { return getString("ExplicitScheme.4"); } - + @Override public Class[] domain() { return new Class[]{ParticipatingMedium.class}; @@ -148,17 +148,17 @@ public Class[] domain() { public void setCalculationStatus(RTECalculationStatus calculationStatus) throws SolverException { this.status = calculationStatus; if (status != RTECalculationStatus.NORMAL) { - throw new SolverException(status.toString(), + throw new SolverException(status.toString(), RTE_SOLVER_ERROR); } } - + public final boolean isAutoUpdateFluxes() { return this.autoUpdateFluxes; } - + public final void setAutoUpdateFluxes(boolean auto) { this.autoUpdateFluxes = auto; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolverNL.java b/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolverNL.java index ab30eeb..6d40c3b 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolverNL.java +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolverNL.java @@ -28,24 +28,24 @@ * * @author Artem Lunev */ -public class ExplicitCoupledSolverNL extends ExplicitCoupledSolver - implements FixedPointIterations -{ +public class ExplicitCoupledSolverNL extends ExplicitCoupledSolver + implements FixedPointIterations { + private static final long serialVersionUID = 4214528397984492532L; private double nonlinearPrecision; - + public ExplicitCoupledSolverNL() { super(); nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); setAutoUpdateFluxes(false); } - + public ExplicitCoupledSolverNL(NumericProperty N, NumericProperty timeFactor) { super(N, timeFactor); nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); setAutoUpdateFluxes(false); } - + @Override public void timeStep(int m) throws SolverException { doIterations(getCurrentSolution(), nonlinearPrecision, m); @@ -61,13 +61,13 @@ public void finaliseIteration(double[] V) throws SolverException { FixedPointIterations.super.finaliseIteration(V); setCalculationStatus(this.getCoupling().getRadiativeTransferEquation().compute(V)); } - + @Override public DifferenceScheme copy() { var grid = getGrid(); return new ExplicitCoupledSolverNL(grid.getGridDensity(), grid.getTimeFactor()); } - + public final NumericProperty getNonlinearPrecision() { return derive(NONLINEAR_PRECISION, nonlinearPrecision); } @@ -84,10 +84,10 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { super.set(type, property); } } - + @Override public String toString() { return Messages.getString("ExplicitScheme.5"); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/problem/schemes/solvers/ExplicitLinearisedSolver.java b/src/main/java/pulse/problem/schemes/solvers/ExplicitLinearisedSolver.java index 7b7f0ae..cd667be 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ExplicitLinearisedSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitLinearisedSolver.java @@ -46,6 +46,7 @@ */ public class ExplicitLinearisedSolver extends ExplicitScheme implements Solver { + private static final long serialVersionUID = 3084350485569519036L; private int N; private double hx; private double a; @@ -66,8 +67,8 @@ public ExplicitLinearisedSolver(NumericProperty N, NumericProperty timeFactor, N @Override public void prepare(Problem problem) throws SolverException { super.prepare(problem); - - zeta = (double) ( (ClassicalProblem) problem).getGeometricFactor().getValue(); + + zeta = (double) ((ClassicalProblem) problem).getGeometricFactor().getValue(); N = (int) getGrid().getGridDensity().getValue(); hx = getGrid().getXStep(); diff --git a/src/main/java/pulse/problem/schemes/solvers/ExplicitNonlinearSolver.java b/src/main/java/pulse/problem/schemes/solvers/ExplicitNonlinearSolver.java index ed8e7fa..546abc0 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ExplicitNonlinearSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitNonlinearSolver.java @@ -19,6 +19,7 @@ public class ExplicitNonlinearSolver extends ExplicitScheme implements Solver, FixedPointIterations { + private static final long serialVersionUID = -5392648684733420360L; private int N; private double hx; diff --git a/src/main/java/pulse/problem/schemes/solvers/ExplicitTranslucentSolver.java b/src/main/java/pulse/problem/schemes/solvers/ExplicitTranslucentSolver.java index 56e747c..ac48b36 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ExplicitTranslucentSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitTranslucentSolver.java @@ -13,6 +13,7 @@ public class ExplicitTranslucentSolver extends ExplicitScheme implements Solver { + private static final long serialVersionUID = -3693226611473383024L; private int N; private double hx; private double tau; @@ -33,7 +34,7 @@ public void prepare(Problem problem) throws SolverException { super.prepare(problem); var grid = getGrid(); - model = ((PenetrationProblem)problem).getAbsorptionModel(); + model = ((PenetrationProblem) problem).getAbsorptionModel(); N = (int) grid.getGridDensity().getValue(); hx = grid.getXStep(); diff --git a/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolver.java b/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolver.java index e9e4f60..a42bac2 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolver.java @@ -17,7 +17,7 @@ import pulse.problem.statements.model.ThermoOpticalProperties; import pulse.properties.NumericProperty; -public abstract class ImplicitCoupledSolver extends CoupledImplicitScheme +public abstract class ImplicitCoupledSolver extends CoupledImplicitScheme implements Solver { private RadiativeTransferSolver rte; @@ -50,7 +50,7 @@ public void prepare(Problem problem) throws SolverException { final var grid = getGrid(); var coupling = getCoupling(); - coupling.init( (ParticipatingMedium) problem, grid); + coupling.init((ParticipatingMedium) problem, grid); rte = coupling.getRadiativeTransferEquation(); N = (int) getGrid().getGridDensity().getValue(); @@ -72,7 +72,7 @@ public void prepare(Problem problem) throws SolverException { v1 = 1.0 + HX2_2TAU + hx * Bi1; fluxes = rte.getFluxes(); - + var tridiagonal = new TridiagonalMatrixAlgorithm(grid) { @Override @@ -91,8 +91,8 @@ public double phi(int i) { tridiagonal.evaluateAlpha(); setTridiagonalMatrixAlgorithm(tridiagonal); - - zeta = (double) ((ClassicalProblem)problem).getGeometricFactor().getValue(); + + zeta = (double) ((ClassicalProblem) problem).getGeometricFactor().getValue(); } @Override @@ -104,7 +104,7 @@ public void solve(ParticipatingMedium problem) throws SolverException { var status = getCalculationStatus(); if (status != RTECalculationStatus.NORMAL) { - throw new SolverException(status.toString(), + throw new SolverException(status.toString(), RTE_SOLVER_ERROR); } diff --git a/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolverNL.java b/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolverNL.java index 389d148..af0d6e1 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolverNL.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolverNL.java @@ -30,6 +30,7 @@ */ public class ImplicitCoupledSolverNL extends ImplicitCoupledSolver implements FixedPointIterations { + private static final long serialVersionUID = -3993380888844448942L; private double nonlinearPrecision; public ImplicitCoupledSolverNL() { @@ -43,7 +44,7 @@ public ImplicitCoupledSolverNL(NumericProperty N, NumericProperty timeFactor, Nu nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); setAutoUpdateFluxes(false); } - + @Override public void timeStep(final int m) throws SolverException { doIterations(getCurrentSolution(), nonlinearPrecision, m); @@ -77,16 +78,16 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { super.set(type, property); } } - + @Override public DifferenceScheme copy() { var grid = getGrid(); return new ImplicitCoupledSolverNL(grid.getGridDensity(), grid.getTimeFactor(), getTimeLimit()); } - + @Override public String toString() { return Messages.getString("ImplicitScheme.5"); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/problem/schemes/solvers/ImplicitDiathermicSolver.java b/src/main/java/pulse/problem/schemes/solvers/ImplicitDiathermicSolver.java index 5343958..587271b 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitDiathermicSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitDiathermicSolver.java @@ -18,7 +18,7 @@ public class ImplicitDiathermicSolver extends ImplicitScheme implements Solver[] domain() { return new Class[]{DiathermicMedium.class}; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/solvers/ImplicitLinearisedSolver.java b/src/main/java/pulse/problem/schemes/solvers/ImplicitLinearisedSolver.java index 16a80d1..62b5709 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitLinearisedSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitLinearisedSolver.java @@ -45,16 +45,18 @@ * @param a subclass of ClassicalProblem * @see super.solve(Problem) */ -public class ImplicitLinearisedSolver extends ImplicitScheme +public class ImplicitLinearisedSolver extends ImplicitScheme implements Solver { + private static final long serialVersionUID = -5182202341972279175L; + private int N; - + protected double Bi1HTAU; protected double tau; protected double HH; protected double _2HTAU; - + private double zeta; public ImplicitLinearisedSolver() { @@ -78,8 +80,8 @@ public void prepare(Problem problem) throws SolverException { N = (int) grid.getGridDensity().getValue(); final double hx = grid.getXStep(); tau = grid.getTimeStep(); - - zeta = (double) ((ClassicalProblem)problem).getGeometricFactor().getValue(); + + zeta = (double) ((ClassicalProblem) problem).getGeometricFactor().getValue(); final double Bi1 = (double) problem.getProperties().getHeatLoss().getValue(); @@ -114,7 +116,7 @@ public double firstBeta() { @Override public double evalRightBoundary(final double alphaN, final double betaN) { - return (HH * getPreviousSolution()[N] + 2. * tau * betaN + return (HH * getPreviousSolution()[N] + 2. * tau * betaN + _2HTAU * (1.0 - zeta) * getCurrentPulseValue() //additional term due to stray light ) / (2 * Bi1HTAU + HH - 2. * tau * (alphaN - 1)); } @@ -130,4 +132,4 @@ public Class[] domain() { return new Class[]{ClassicalProblem.class}; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/solvers/ImplicitNonlinearSolver.java b/src/main/java/pulse/problem/schemes/solvers/ImplicitNonlinearSolver.java index 0cc048a..20224b8 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitNonlinearSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitNonlinearSolver.java @@ -17,6 +17,7 @@ public class ImplicitNonlinearSolver extends ImplicitScheme implements Solver, FixedPointIterations { + private static final long serialVersionUID = -6263519219698662707L; private int N; private double HH; private double tau; @@ -139,7 +140,7 @@ public void iteration(int m) throws SolverException { @Override public double evalRightBoundary(double alphaN, double betaN) { - return c2 * (2. * betaN * tau + HH * getPreviousSolution()[N] + return c2 * (2. * betaN * tau + HH * getPreviousSolution()[N] + c1 * (fastPowLoop(getCurrentSolution()[N] * dT_T + 1, 4) - 1)); } diff --git a/src/main/java/pulse/problem/schemes/solvers/ImplicitTranslucentSolver.java b/src/main/java/pulse/problem/schemes/solvers/ImplicitTranslucentSolver.java index d5005ba..7ace1f8 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitTranslucentSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitTranslucentSolver.java @@ -14,6 +14,7 @@ public class ImplicitTranslucentSolver extends ImplicitScheme implements Solver { + private static final long serialVersionUID = -2207434474904484692L; private AbsorptionModel absorption; private int N; @@ -40,12 +41,12 @@ public void prepare(Problem problem) throws SolverException { final double Bi1H = (double) problem.getProperties().getHeatLoss().getValue() * grid.getXStep(); final double hx = grid.getXStep(); - absorption = ((PenetrationProblem)problem).getAbsorptionModel(); - + absorption = ((PenetrationProblem) problem).getAbsorptionModel(); + HH = hx * hx; _2Bi1HTAU = 2.0 * Bi1H * tau; b11 = 1.0 / (1.0 + 2.0 * tau / HH * (1 + Bi1H)); - + var tridiagonal = new TridiagonalMatrixAlgorithm(grid) { @Override @@ -54,7 +55,7 @@ public double phi(final int i) { } }; - + // coefficients for difference equation tridiagonal.setCoefA(1. / HH); tridiagonal.setCoefB(1. / tau + 2. / HH); @@ -81,7 +82,7 @@ public double evalRightBoundary(final double alphaN, final double betaN) { final double tau = getGrid().getTimeStep(); var tridiagonal = this.getTridiagonalMatrixAlgorithm(); - return (HH * getPreviousSolution()[N] + HH*tau*tridiagonal.phi(N) + return (HH * getPreviousSolution()[N] + HH * tau * tridiagonal.phi(N) + 2. * tau * betaN) / (_2Bi1HTAU + HH + 2. * tau * (1 - alphaN)); } @@ -89,7 +90,7 @@ public double evalRightBoundary(final double alphaN, final double betaN) { public double firstBeta() { var tridiagonal = this.getTridiagonalMatrixAlgorithm(); double tau = getGrid().getTimeStep(); - return (getPreviousSolution()[0] + tau*tridiagonal.phi(0))* b11; + return (getPreviousSolution()[0] + tau * tridiagonal.phi(0)) * b11; } @Override @@ -113,4 +114,4 @@ public Class[] domain() { return new Class[]{PenetrationProblem.class}; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/solvers/ImplicitTwoTemperatureSolver.java b/src/main/java/pulse/problem/schemes/solvers/ImplicitTwoTemperatureSolver.java index 1a09949..b31f7fd 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitTwoTemperatureSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitTwoTemperatureSolver.java @@ -22,6 +22,7 @@ public class ImplicitTwoTemperatureSolver extends ImplicitScheme implements Solver, FixedPointIterations { + private static final long serialVersionUID = 7955478815933535623L; private AbsorptionModel absorption; private TridiagonalMatrixAlgorithm gasSolver; diff --git a/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolver.java b/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolver.java index fc3e516..4ba9ea2 100644 --- a/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolver.java @@ -19,7 +19,7 @@ import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -public abstract class MixedCoupledSolver extends CoupledImplicitScheme +public abstract class MixedCoupledSolver extends CoupledImplicitScheme implements Solver { private RadiativeTransferSolver rte; @@ -74,13 +74,13 @@ public void prepare(Problem problem) throws SolverException { hx = grid.getXStep(); tau = grid.getTimeStep(); - var properties = (ThermoOpticalProperties)problem.getProperties(); + var properties = (ThermoOpticalProperties) problem.getProperties(); //combined biot - Bi1 = (double) properties.getHeatLoss().getValue() + - (double) properties.getConvectiveLosses().getValue(); + Bi1 = (double) properties.getHeatLoss().getValue() + + (double) properties.getConvectiveLosses().getValue(); + + zeta = (double) ((ClassicalProblem) problem).getGeometricFactor().getValue(); - zeta = (double) ( (ClassicalProblem)problem ).getGeometricFactor().getValue(); - var tridiagonal = new TridiagonalMatrixAlgorithm(grid) { @Override @@ -95,7 +95,7 @@ public double beta(final double f, final double phi, final int i) { var U = getPreviousSolution(); return super.beta(f + ONE_MINUS_SIGMA * (U[i] - 2.0 * U[i - 1] + U[i - 2]) / HX2, TAU0_NP * phi, i); } - + @Override public void evaluateBeta(final double[] U) { var fluxes = rte.getFluxes(); @@ -168,10 +168,10 @@ public double firstBeta() { var U = getPreviousSolution(); final double phi = TAU0_NP * fluxes.fluxDerivativeFront(); return (_2TAUHX - * (getCurrentPulseValue() * zeta - SIGMA_NP * fluxes.getFlux(0) + * (getCurrentPulseValue() * zeta - SIGMA_NP * fluxes.getFlux(0) - ONE_MINUS_SIGMA_NP * fluxes.getStoredFlux(0)) - + HX2 * (U[0] + phi * tau) + _2TAU_ONE_MINUS_SIGMA * - (U[1] - U[0] * ONE_PLUS_Bi1_HX)) * BETA1_FACTOR; + + HX2 * (U[0] + phi * tau) + _2TAU_ONE_MINUS_SIGMA + * (U[1] - U[0] * ONE_PLUS_Bi1_HX)) * BETA1_FACTOR; } @Override @@ -215,4 +215,4 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { } } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolverNL.java b/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolverNL.java index 77d7b19..94ad40d 100644 --- a/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolverNL.java +++ b/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolverNL.java @@ -30,6 +30,7 @@ */ public class MixedCoupledSolverNL extends MixedCoupledSolver implements FixedPointIterations { + private static final long serialVersionUID = -8344384560376683594L; private double nonlinearPrecision; public MixedCoupledSolverNL() { @@ -43,7 +44,7 @@ public MixedCoupledSolverNL(NumericProperty N, NumericProperty timeFactor, Numer nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); setAutoUpdateFluxes(false); } - + @Override public void timeStep(final int m) throws SolverException { doIterations(getCurrentSolution(), nonlinearPrecision, m); @@ -77,16 +78,16 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { super.set(type, property); } } - + @Override public DifferenceScheme copy() { var grid = getGrid(); return new MixedCoupledSolverNL(grid.getGridDensity(), grid.getTimeFactor(), getTimeLimit()); } - + @Override public String toString() { return Messages.getString("MixedScheme2.5"); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/problem/schemes/solvers/MixedLinearisedSolver.java b/src/main/java/pulse/problem/schemes/solvers/MixedLinearisedSolver.java index 56a74f0..e2fc25c 100644 --- a/src/main/java/pulse/problem/schemes/solvers/MixedLinearisedSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/MixedLinearisedSolver.java @@ -50,12 +50,13 @@ */ public class MixedLinearisedSolver extends MixedScheme implements Solver { + private static final long serialVersionUID = 2233988060956648641L; private double b1; private double b2; private double b3; private double c1; private double c2; - + private double zeta; private final static double EPS = 1e-7; // a small value ensuring numeric stability @@ -93,8 +94,8 @@ public void prepare(Problem problem) throws SolverException { b3 = hx * tau; c1 = b2; c2 = Bi1HTAU + HH; - - zeta = (double) ((ClassicalProblem)problem).getGeometricFactor().getValue(); + + zeta = (double) ((ClassicalProblem) problem).getGeometricFactor().getValue(); var tridiagonal = new TridiagonalMatrixAlgorithm(grid) { @@ -128,15 +129,15 @@ public double evalRightBoundary(final double alphaN, final double betaN) { final double tau = grid.getTimeStep(); final int N = (int) grid.getGridDensity().getValue(); - return ( c1 * U[N] + tau * betaN + b3 * (1.0 - zeta) * getCurrentPulseValue() - - tau * (U[N] - U[N - 1]) ) / (c2 - tau * (alphaN - 1)); + return (c1 * U[N] + tau * betaN + b3 * (1.0 - zeta) * getCurrentPulseValue() + - tau * (U[N] - U[N - 1])) / (c2 - tau * (alphaN - 1)); } - + @Override public double pulse(int m) { final double tau = getGrid().getTimeStep(); var pulse = getDiscretePulse(); - return pulse.laserPowerAt((m - 1 + EPS) * tau) + pulse.laserPowerAt((m - EPS) * tau); + return pulse.laserPowerAt((m - 1 + EPS) * tau) + pulse.laserPowerAt((m - EPS) * tau); } @Override diff --git a/src/main/java/pulse/problem/schemes/solvers/Solver.java b/src/main/java/pulse/problem/schemes/solvers/Solver.java index f371b18..654f897 100644 --- a/src/main/java/pulse/problem/schemes/solvers/Solver.java +++ b/src/main/java/pulse/problem/schemes/solvers/Solver.java @@ -1,5 +1,6 @@ package pulse.problem.schemes.solvers; +import java.io.Serializable; import pulse.problem.statements.Problem; /** @@ -9,7 +10,7 @@ * * @param an instance of Problem */ -public interface Solver { +public interface Solver extends Serializable { /** * Calculates the solution of the {@code t} and stores it in the respective diff --git a/src/main/java/pulse/problem/schemes/solvers/SolverException.java b/src/main/java/pulse/problem/schemes/solvers/SolverException.java index 8aacbe0..ef97e32 100644 --- a/src/main/java/pulse/problem/schemes/solvers/SolverException.java +++ b/src/main/java/pulse/problem/schemes/solvers/SolverException.java @@ -2,22 +2,22 @@ @SuppressWarnings("serial") public class SolverException extends Exception { - + private final SolverExceptionType type; public SolverException(String status, SolverExceptionType type) { super(status); this.type = type; } - + public SolverException(SolverExceptionType type) { this(type.toString(), type); } - + public SolverExceptionType getType() { return type; } - + public enum SolverExceptionType { RTE_SOLVER_ERROR, OPTIMISATION_ERROR, @@ -26,4 +26,4 @@ public enum SolverExceptionType { ILLEGAL_PARAMETERS, } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/statements/AdiabaticSolution.java b/src/main/java/pulse/problem/statements/AdiabaticSolution.java index a335fac..8429374 100644 --- a/src/main/java/pulse/problem/statements/AdiabaticSolution.java +++ b/src/main/java/pulse/problem/statements/AdiabaticSolution.java @@ -1,5 +1,6 @@ package pulse.problem.statements; +import java.io.Serializable; import static java.lang.Math.PI; import static java.lang.Math.exp; import static java.lang.Math.pow; @@ -9,8 +10,9 @@ import pulse.HeatingCurve; import pulse.problem.statements.model.ThermalProperties; -public class AdiabaticSolution { +public class AdiabaticSolution implements Serializable { + private static final long serialVersionUID = 4240406501288696621L; public final static int DEFAULT_CLASSIC_PRECISION = 200; public final static int DEFAULT_POINTS = 100; @@ -75,7 +77,7 @@ public static HeatingCurve classicSolution(Problem p, double timeLimit, int prec private final static double solutionAt(ThermalProperties p, double time, int precision) { final double EPS = 1E-8; - final double Fo = time / p.timeFactor(); + final double Fo = time / p.characteristicTime(); if (time < EPS) { return 0; diff --git a/src/main/java/pulse/problem/statements/ClassicalProblem.java b/src/main/java/pulse/problem/statements/ClassicalProblem.java index 7243985..2f5fd36 100644 --- a/src/main/java/pulse/problem/statements/ClassicalProblem.java +++ b/src/main/java/pulse/problem/statements/ClassicalProblem.java @@ -24,6 +24,10 @@ */ public class ClassicalProblem extends Problem { + /** + * + */ + private static final long serialVersionUID = -1915004757733565502L; private double bias; public ClassicalProblem() { diff --git a/src/main/java/pulse/problem/statements/ClassicalProblem2D.java b/src/main/java/pulse/problem/statements/ClassicalProblem2D.java index 74b74e0..3ea99b1 100644 --- a/src/main/java/pulse/problem/statements/ClassicalProblem2D.java +++ b/src/main/java/pulse/problem/statements/ClassicalProblem2D.java @@ -28,7 +28,12 @@ * pulse-to-diameter ratio. * */ -public class ClassicalProblem2D extends Problem { +public class ClassicalProblem2D extends ClassicalProblem { + + /** + * + */ + private static final long serialVersionUID = 8974995052071820422L; public ClassicalProblem2D() { super(); @@ -66,7 +71,7 @@ public String toString() { public DiscretePulse discretePulseOn(Grid grid) { return grid instanceof Grid2D ? new DiscretePulse2D(this, (Grid2D) grid) : super.discretePulseOn(grid); } - + @Override public void optimisationVector(ParameterVector output) { super.optimisationVector(output); @@ -78,16 +83,18 @@ public void optimisationVector(ParameterVector output) { var key = p.getIdentifier().getKeyword(); Transformable transform = new InvDiamTransform(properties); var bounds = Segment.boundsFrom(key); - + switch (key) { case FOV_OUTER: value = (double) properties.getFOVOuter().getValue(); + transform = new StickTransform(bounds); break; case FOV_INNER: value = (double) properties.getFOVInner().getValue(); break; case SPOT_DIAMETER: value = (double) ((Pulse2D) getPulse()).getSpotDiameter().getValue(); + transform = new StickTransform(bounds); break; case HEAT_LOSS_SIDE: value = (double) properties.getSideLosses().getValue(); @@ -125,7 +132,7 @@ public void assign(ParameterVector params) throws SolverException { properties.set(type, derive(type, p.inverseTransform())); break; case SPOT_DIAMETER: - ((Pulse2D) getPulse()).setSpotDiameter(derive(SPOT_DIAMETER, + ((Pulse2D) getPulse()).setSpotDiameter(derive(SPOT_DIAMETER, p.inverseTransform())); break; default: @@ -143,4 +150,4 @@ public Problem copy() { return new ClassicalProblem2D(this); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/statements/DiathermicMedium.java b/src/main/java/pulse/problem/statements/DiathermicMedium.java index dc4bf9b..008a54f 100644 --- a/src/main/java/pulse/problem/statements/DiathermicMedium.java +++ b/src/main/java/pulse/problem/statements/DiathermicMedium.java @@ -33,6 +33,8 @@ */ public class DiathermicMedium extends ClassicalProblem { + private static final long serialVersionUID = -98674255799114512L; + public DiathermicMedium() { super(); } @@ -69,18 +71,18 @@ public void optimisationVector(ParameterVector output) { break; case HEAT_LOSS_CONVECTIVE: bounds = Segment.boundsFrom(HEAT_LOSS_CONVECTIVE); - value = (double) properties.getConvectiveLosses().getValue(); - break; + value = (double) properties.getConvectiveLosses().getValue(); + break; case HEAT_LOSS: - if(properties.areThermalPropertiesLoaded()) { - value = (double) properties.getHeatLoss().getValue(); - bounds = new Segment(0.0, properties.maxRadiationBiot() ); - break; - } + if (properties.areThermalPropertiesLoaded()) { + value = (double) properties.getHeatLoss().getValue(); + bounds = new Segment(0.0, properties.maxRadiationBiot()); + break; + } default: continue; } - + p.setTransform(new StickTransform(bounds)); p.setValue(value); p.setBounds(bounds); @@ -101,11 +103,11 @@ public void assign(ParameterVector params) throws SolverException { switch (key) { case DIATHERMIC_COEFFICIENT: - properties.setDiathermicCoefficient(derive(DIATHERMIC_COEFFICIENT, + properties.setDiathermicCoefficient(derive(DIATHERMIC_COEFFICIENT, p.inverseTransform())); break; case HEAT_LOSS_CONVECTIVE: - properties.setConvectiveLosses(derive(HEAT_LOSS_CONVECTIVE, + properties.setConvectiveLosses(derive(HEAT_LOSS_CONVECTIVE, p.inverseTransform())); break; default: @@ -130,4 +132,4 @@ public DiathermicMedium copy() { return new DiathermicMedium(this); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/statements/NonlinearProblem.java b/src/main/java/pulse/problem/statements/NonlinearProblem.java index f21b3b8..d3e0187 100644 --- a/src/main/java/pulse/problem/statements/NonlinearProblem.java +++ b/src/main/java/pulse/problem/statements/NonlinearProblem.java @@ -25,6 +25,11 @@ public class NonlinearProblem extends ClassicalProblem { + /** + * + */ + private static final long serialVersionUID = -5266939533182313886L; + public NonlinearProblem() { super(); setPulse(new Pulse2D()); @@ -68,14 +73,15 @@ public NumericProperty getThermalConductivity() { } /** - * - * Does the same as super-class method plus updates the laser energy, if needed. + * + * Does the same as super-class method plus updates the laser energy, if + * needed. + * * @param params * @throws pulse.problem.schemes.solvers.SolverException * @see pulse.problem.statements.Problem.getPulse() - * - */ - + * + */ @Override public void assign(ParameterVector params) throws SolverException { super.assign(params); @@ -92,29 +98,30 @@ public void assign(ParameterVector params) throws SolverException { } } - + /** - * - * Does the same as super-class method plus extracts the laser energy and stores it in the {@code output}, if needed. + * + * Does the same as super-class method plus extracts the laser energy and + * stores it in the {@code output}, if needed. + * * @param output * @param flags * @see pulse.problem.statements.Problem.getPulse() - * + * */ - @Override public void optimisationVector(ParameterVector output) { super.optimisationVector(output); - + for (Parameter p : output.getParameters()) { var key = p.getIdentifier().getKeyword(); - if(key == LASER_ENERGY) { + if (key == LASER_ENERGY) { var bounds = Segment.boundsFrom(LASER_ENERGY); p.setBounds(bounds); p.setTransform(new StickTransform(bounds)); - p.setValue( (double) getPulse().getLaserEnergy().getValue()); + p.setValue((double) getPulse().getLaserEnergy().getValue()); } } @@ -131,4 +138,4 @@ public Problem copy() { return new NonlinearProblem(this); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/statements/ParticipatingMedium.java b/src/main/java/pulse/problem/statements/ParticipatingMedium.java index e8497e2..844c904 100644 --- a/src/main/java/pulse/problem/statements/ParticipatingMedium.java +++ b/src/main/java/pulse/problem/statements/ParticipatingMedium.java @@ -1,7 +1,5 @@ package pulse.problem.statements; - -import java.util.List; import java.util.Set; import pulse.math.ParameterVector; @@ -10,13 +8,14 @@ import pulse.problem.schemes.solvers.SolverException; import pulse.problem.statements.model.ThermalProperties; import pulse.problem.statements.model.ThermoOpticalProperties; -import pulse.properties.Flag; import pulse.properties.NumericPropertyKeyword; import static pulse.properties.NumericPropertyKeyword.SOURCE_GEOMETRIC_FACTOR; import pulse.ui.Messages; public class ParticipatingMedium extends NonlinearProblem { + private static final long serialVersionUID = -8227061869299826343L; + public ParticipatingMedium() { super(); setComplexity(ProblemComplexity.HIGH); @@ -73,4 +72,4 @@ public Problem copy() { return new ParticipatingMedium(this); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/statements/PenetrationProblem.java b/src/main/java/pulse/problem/statements/PenetrationProblem.java index d08d17c..588e277 100644 --- a/src/main/java/pulse/problem/statements/PenetrationProblem.java +++ b/src/main/java/pulse/problem/statements/PenetrationProblem.java @@ -17,6 +17,7 @@ public class PenetrationProblem extends ClassicalProblem { + private static final long serialVersionUID = -6760177658036060627L; private InstanceDescriptor instanceDescriptor = new InstanceDescriptor<>( "Absorption Model Selector", AbsorptionModel.class); @@ -58,7 +59,7 @@ public List listedTypes() { list.add(instanceDescriptor); return list; } - + @Override public Set listedKeywords() { var set = super.listedKeywords(); @@ -97,4 +98,4 @@ public Problem copy() { return new PenetrationProblem(this); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/statements/Problem.java b/src/main/java/pulse/problem/statements/Problem.java index f228515..7392e27 100644 --- a/src/main/java/pulse/problem/statements/Problem.java +++ b/src/main/java/pulse/problem/statements/Problem.java @@ -1,11 +1,13 @@ package pulse.problem.statements; +import java.io.Serializable; import java.util.Arrays; import static pulse.input.listeners.CurveEventType.RESCALED; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericPropertyKeyword.TIME_SHIFT; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.Executors; import java.util.stream.Collectors; @@ -51,6 +53,10 @@ */ public abstract class Problem extends PropertyHolder implements Reflexive, Optimisable { + /** + * + */ + private static final long serialVersionUID = 7275327427201737684L; private ThermalProperties properties; private HeatingCurve curve; private Baseline baseline; @@ -173,7 +179,7 @@ public final void setPulse(Pulse pulse) { * @param c the {@code ExperimentalData} object */ public void retrieveData(ExperimentalData c) { - baseline.fitTo(c); + baseline.fitTo(c); estimateSignalRange(c); updateProperties(this, c.getMetadata()); properties.useTheoreticalEstimates(c); @@ -245,7 +251,7 @@ public void optimisationVector(ParameterVector output) { p.setTransform(new StickTransform(bounds)); break; case TIME_SHIFT: - double magnitude = 0.25 * properties.timeFactor(); + double magnitude = 0.25 * properties.characteristicTime(); bounds = new Segment(-magnitude, magnitude); value = (double) curve.getTimeShift().getValue(); break; @@ -422,9 +428,8 @@ private void initBaseline() { var searchTask = (SearchTask) this.specificAncestor(SearchTask.class); if (searchTask != null) { var experimentalData = (ExperimentalData) searchTask.getInput(); - Executors.newSingleThreadExecutor().submit(() - -> baseline.fitTo(experimentalData) - ); + Runnable baselineFitter = (Runnable & Serializable) () -> baseline.fitTo(experimentalData); + Executors.newSingleThreadExecutor().submit(baselineFitter); } parameterListChanged(); } diff --git a/src/main/java/pulse/problem/statements/Pulse.java b/src/main/java/pulse/problem/statements/Pulse.java index 1b0fbf3..f177b20 100644 --- a/src/main/java/pulse/problem/statements/Pulse.java +++ b/src/main/java/pulse/problem/statements/Pulse.java @@ -7,7 +7,6 @@ import static pulse.properties.NumericPropertyKeyword.PULSE_WIDTH; import java.util.List; -import java.util.Objects; import java.util.Set; import pulse.input.ExperimentalData; @@ -31,6 +30,10 @@ */ public class Pulse extends PropertyHolder { + /** + * + */ + private static final long serialVersionUID = 3564455042006771282L; private double pulseWidth; private double laserEnergy; @@ -51,11 +54,8 @@ public Pulse() { laserEnergy = (double) def(LASER_ENERGY).getValue(); instanceDescriptor.setSelectedDescriptor(RectangularPulse.class.getSimpleName()); initShape(); - instanceDescriptor.addListener(() -> { - initShape(); - this.firePropertyChanged(instanceDescriptor, instanceDescriptor); - }); - addListeners(); + addInstanceListener(); + initListeners(); } /** @@ -68,14 +68,20 @@ public Pulse(Pulse p) { this.pulseShape = p.getPulseShape(); this.pulseWidth = p.pulseWidth; this.laserEnergy = p.laserEnergy; - addListeners(); + addInstanceListener(); + initListeners(); } - private void addListeners() { + private void addInstanceListener() { instanceDescriptor.addListener(() -> { initShape(); this.firePropertyChanged(instanceDescriptor, instanceDescriptor); }); + } + + @Override + public void initListeners() { + super.initListeners(); addListener((PropertyEvent event) -> { //when a property of the pulse is changed @@ -92,16 +98,16 @@ private void addListeners() { NumericProperty pw = NumericProperties .derive(NumericPropertyKeyword.LOWER_BOUND, (Number) np.getValue()); - - var range = ( (ExperimentalData) corrTask.getInput() ).getRange(); - - if( range.getLowerBound().compareTo(pw) < 0 ) { - - //update lower bound of the range for that SearchTask - range.setLowerBound(pw); - + + var range = ((ExperimentalData) corrTask.getInput()).getRange(); + + if (range.getLowerBound().compareTo(pw) < 0) { + + //update lower bound of the range for that SearchTask + range.setLowerBound(pw); + } - + } } @@ -132,24 +138,24 @@ public void setPulseWidth(NumericProperty pulseWidth) { requireType(pulseWidth, PULSE_WIDTH); double newValue = (double) pulseWidth.getValue(); - + double relChange = Math.abs((newValue - this.pulseWidth) / (this.pulseWidth + newValue)); final double EPS = 1E-3; - + //do not update -- if new value is the same as the previous one if (relChange > EPS && newValue > 0) { - + //validate -- do not update if the new pulse width is greater than 2 half-times - SearchTask task = (SearchTask) this.specificAncestor(SearchTask.class); - ExperimentalData data = (ExperimentalData) task.getInput(); - - if(newValue < 2.0 * data.getHalfTimeCalculator().getHalfTime()) { + SearchTask task = (SearchTask) this.specificAncestor(SearchTask.class); + ExperimentalData data = (ExperimentalData) task.getInput(); + + if (newValue < 2.0 * data.getHalfTimeCalculator().getHalfTime()) { this.pulseWidth = (double) pulseWidth.getValue(); firePropertyChanged(this, pulseWidth); } - + } - + } public NumericProperty getLaserEnergy() { @@ -164,12 +170,10 @@ public void setLaserEnergy(NumericProperty laserEnergy) { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getPulseShape()); - sb.append(" ; "); - sb.append(getPulseWidth()); - sb.append(" ; "); - sb.append(getLaserEnergy()); + StringBuilder sb = new StringBuilder("Pulse:"); + sb.append(String.format("%n %-25s", getPulseShape())); + sb.append(String.format("%n %-25s", getPulseWidth())); + sb.append(String.format("%n %-25s", getLaserEnergy())); return sb.toString(); } @@ -217,7 +221,8 @@ public PulseTemporalShape getPulseShape() { public void setPulseShape(PulseTemporalShape pulseShape) { this.pulseShape = pulseShape; - pulseShape.setParent(this); + pulseShape.setParent(this); + } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/problem/statements/Pulse2D.java b/src/main/java/pulse/problem/statements/Pulse2D.java index 16895b6..7dd4c37 100644 --- a/src/main/java/pulse/problem/statements/Pulse2D.java +++ b/src/main/java/pulse/problem/statements/Pulse2D.java @@ -12,6 +12,7 @@ public class Pulse2D extends Pulse { + private static final long serialVersionUID = 8753396877032808678L; private double spotDiameter; /** @@ -60,8 +61,7 @@ public void setSpotDiameter(NumericProperty spotDiameter) { @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); - sb.append(" ; "); - sb.append(getSpotDiameter()); + sb.append(String.format("%n %-25s", getSpotDiameter())); return sb.toString(); } diff --git a/src/main/java/pulse/problem/statements/TwoTemperatureModel.java b/src/main/java/pulse/problem/statements/TwoTemperatureModel.java index 72298d7..980814f 100644 --- a/src/main/java/pulse/problem/statements/TwoTemperatureModel.java +++ b/src/main/java/pulse/problem/statements/TwoTemperatureModel.java @@ -23,11 +23,13 @@ public class TwoTemperatureModel extends PenetrationProblem { + private static final long serialVersionUID = 2567125396986165234L; + private Gas gas; private InstanceDescriptor instanceDescriptor = new InstanceDescriptor<>("Gas Selector", Gas.class); - + public TwoTemperatureModel() { super(); setComplexity(ProblemComplexity.MODERATE); @@ -92,7 +94,7 @@ public void optimisationVector(ParameterVector output) { continue; } - p.setTransform(new StickTransform(bounds)); + p.setTransform(new StickTransform(bounds)); p.setValue(value); p.setBounds(bounds); @@ -109,7 +111,7 @@ public void assign(ParameterVector params) throws SolverException { var key = p.getIdentifier().getKeyword(); var np = derive(key, p.inverseTransform()); - + switch (key) { case SOLID_EXCHANGE_COEFFICIENT: ttp.setSolidExchangeCoefficient(np); @@ -124,7 +126,7 @@ public void assign(ParameterVector params) throws SolverException { } } - + } @Override diff --git a/src/main/java/pulse/problem/statements/model/AbsorptionModel.java b/src/main/java/pulse/problem/statements/model/AbsorptionModel.java index 9165b08..9e895e4 100644 --- a/src/main/java/pulse/problem/statements/model/AbsorptionModel.java +++ b/src/main/java/pulse/problem/statements/model/AbsorptionModel.java @@ -25,15 +25,16 @@ public abstract class AbsorptionModel extends PropertyHolder implements Reflexive, Optimisable { + private static final long serialVersionUID = -8937860285061335131L; private Map absorptionMap; - + protected AbsorptionModel() { setPrefix("Absorption model"); absorptionMap = new HashMap<>(); absorptionMap.put(LASER, def(LASER_ABSORPTIVITY)); absorptionMap.put(THERMAL, def(THERMAL_ABSORPTIVITY)); } - + protected AbsorptionModel(AbsorptionModel c) { this.absorptionMap = new HashMap<>(); this.absorptionMap.putAll(c.absorptionMap); @@ -95,7 +96,10 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { @Override public String toString() { - return getClass().getSimpleName() + " : " + absorptionMap.get(LASER) + " ; " + absorptionMap.get(THERMAL); + var sb = new StringBuilder(getSimpleName()); + sb.append(String.format("%n %-25s", absorptionMap.get(LASER))); + sb.append(String.format("%n %-25s", absorptionMap.get(THERMAL))); + return sb.toString(); } @Override @@ -106,7 +110,7 @@ public Set listedKeywords() { set.add(COMBINED_ABSORPTIVITY); return set; } - + @Override public void optimisationVector(ParameterVector output) { for (Parameter p : output.getParameters()) { @@ -114,7 +118,7 @@ public void optimisationVector(ParameterVector output) { double value = 0; Transformable transform = ABS; - + switch (key) { case LASER_ABSORPTIVITY: value = (double) (getLaserAbsorptivity()).getValue(); @@ -158,7 +162,7 @@ public void assign(ParameterVector params) throws SolverException { } } - + public abstract AbsorptionModel copy(); -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/statements/model/BeerLambertAbsorption.java b/src/main/java/pulse/problem/statements/model/BeerLambertAbsorption.java index 3aebab0..6e3a956 100644 --- a/src/main/java/pulse/problem/statements/model/BeerLambertAbsorption.java +++ b/src/main/java/pulse/problem/statements/model/BeerLambertAbsorption.java @@ -2,14 +2,16 @@ public class BeerLambertAbsorption extends AbsorptionModel { + private static final long serialVersionUID = -7996852815508481089L; + public BeerLambertAbsorption() { super(); } - + public BeerLambertAbsorption(AbsorptionModel m) { super(m); } - + @Override public double absorption(SpectralRange range, double y) { double a = (double) (this.getAbsorptivity(range).getValue()); diff --git a/src/main/java/pulse/problem/statements/model/DiathermicProperties.java b/src/main/java/pulse/problem/statements/model/DiathermicProperties.java index bc0a9a5..09f6d17 100644 --- a/src/main/java/pulse/problem/statements/model/DiathermicProperties.java +++ b/src/main/java/pulse/problem/statements/model/DiathermicProperties.java @@ -11,6 +11,7 @@ public class DiathermicProperties extends ThermalProperties { + private static final long serialVersionUID = 1294930368429607512L; private double diathermicCoefficient; private double convectiveLosses; @@ -42,7 +43,7 @@ public void setDiathermicCoefficient(NumericProperty diathermicCoefficient) { requireType(diathermicCoefficient, DIATHERMIC_COEFFICIENT); this.diathermicCoefficient = (double) diathermicCoefficient.getValue(); } - + public NumericProperty getConvectiveLosses() { return derive(HEAT_LOSS_CONVECTIVE, convectiveLosses); } @@ -76,4 +77,4 @@ public Set listedKeywords() { return set; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/statements/model/ExtendedThermalProperties.java b/src/main/java/pulse/problem/statements/model/ExtendedThermalProperties.java index 4ff6ff7..fc720e0 100644 --- a/src/main/java/pulse/problem/statements/model/ExtendedThermalProperties.java +++ b/src/main/java/pulse/problem/statements/model/ExtendedThermalProperties.java @@ -19,6 +19,7 @@ public class ExtendedThermalProperties extends ThermalProperties { + private static final long serialVersionUID = 452882822074681811L; private double d; private double Bi3; private double fovOuter; diff --git a/src/main/java/pulse/problem/statements/model/Gas.java b/src/main/java/pulse/problem/statements/model/Gas.java index 7442ebd..ad050e3 100644 --- a/src/main/java/pulse/problem/statements/model/Gas.java +++ b/src/main/java/pulse/problem/statements/model/Gas.java @@ -1,69 +1,69 @@ package pulse.problem.statements.model; +import java.io.Serializable; import pulse.util.Descriptive; import pulse.util.Reflexive; -public abstract class Gas implements Reflexive, Descriptive { - +public abstract class Gas implements Reflexive, Descriptive, Serializable { + private double conductivity; private double thermalMass; private final int atoms; private final double mass; - + /** * Universal gas constant. */ - public final static double R = 8.314; //J/K/mol - + private final static double ROOM_TEMPERATURE = 300; private final static double NORMAL_PRESSURE = 1E5; - + public Gas(int atoms, double atomicWeight) { evaluate(ROOM_TEMPERATURE, NORMAL_PRESSURE); this.atoms = atoms; - this.mass = atoms * atomicWeight/1e3; + this.mass = atoms * atomicWeight / 1e3; } - + public final void evaluate(double temperature, double pressure) { this.conductivity = thermalConductivity(temperature); this.thermalMass = cp() * density(temperature, pressure); } - + public final void evaluate(double temperature) { evaluate(temperature, NORMAL_PRESSURE); } public final double thermalDiffusivity() { - return conductivity/thermalMass; + return conductivity / thermalMass; } - + public abstract double thermalConductivity(double t); - + public double cp() { return (1.5 + atoms) * R / mass; } - + public double density(double temperature, double pressure) { return pressure * mass / (R * temperature); } - + public double getThermalMass() { return thermalMass; } - + public double getConductivity() { return conductivity; - } - + } + public double getNumberOfAtoms() { return atoms; } - + public double getMolarMass() { return mass; } - + @Override public String toString() { StringBuilder sb = new StringBuilder(getClass().getSimpleName()); @@ -71,5 +71,5 @@ public String toString() { sb.append(String.format("atoms per molecule = %d; atomic weight = %1.4f", atoms, mass)); return sb.toString(); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/problem/statements/model/Helium.java b/src/main/java/pulse/problem/statements/model/Helium.java index 96b71f0..206a2bf 100644 --- a/src/main/java/pulse/problem/statements/model/Helium.java +++ b/src/main/java/pulse/problem/statements/model/Helium.java @@ -1,7 +1,9 @@ package pulse.problem.statements.model; public class Helium extends Gas { - + + private static final long serialVersionUID = -4697854426333597653L; + public Helium() { super(1, 4); } @@ -10,5 +12,5 @@ public Helium() { public double thermalConductivity(double t) { return 0.415 + 0.283E-3 * (t - 1200); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/problem/statements/model/Insulator.java b/src/main/java/pulse/problem/statements/model/Insulator.java index 88c5972..25137f7 100644 --- a/src/main/java/pulse/problem/statements/model/Insulator.java +++ b/src/main/java/pulse/problem/statements/model/Insulator.java @@ -11,16 +11,17 @@ public class Insulator extends AbsorptionModel { + private static final long serialVersionUID = -3519941060924868530L; private double R; public Insulator() { super(); R = (double) def(REFLECTANCE).getValue(); } - + public Insulator(AbsorptionModel m) { super(m); - if(m instanceof Insulator) { + if (m instanceof Insulator) { R = (double) ((Insulator) m).getReflectance().getValue(); } else { R = (double) def(REFLECTANCE).getValue(); diff --git a/src/main/java/pulse/problem/statements/model/Nitrogen.java b/src/main/java/pulse/problem/statements/model/Nitrogen.java index acaef03..2b00b8e 100644 --- a/src/main/java/pulse/problem/statements/model/Nitrogen.java +++ b/src/main/java/pulse/problem/statements/model/Nitrogen.java @@ -1,14 +1,16 @@ package pulse.problem.statements.model; public class Nitrogen extends Gas { - + + private static final long serialVersionUID = -8593450360265855427L; + public Nitrogen() { super(2, 14); } @Override public double thermalConductivity(double t) { - return Math.sqrt(t) * (-92.39/t + 1.647 + 5.255E-4*t) * 1E-3; + return Math.sqrt(t) * (-92.39 / t + 1.647 + 5.255E-4 * t) * 1E-3; } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/problem/statements/model/ThermalProperties.java b/src/main/java/pulse/problem/statements/model/ThermalProperties.java index 153faa1..5e2f135 100644 --- a/src/main/java/pulse/problem/statements/model/ThermalProperties.java +++ b/src/main/java/pulse/problem/statements/model/ThermalProperties.java @@ -2,8 +2,6 @@ import static java.lang.Math.PI; import java.util.List; -import static pulse.input.InterpolationDataset.getDataset; -import static pulse.input.InterpolationDataset.StandartType.HEAT_CAPACITY; import static pulse.properties.NumericProperties.def; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericProperty.requireType; @@ -13,17 +11,18 @@ import java.util.stream.Collectors; import pulse.input.ExperimentalData; -import pulse.input.InterpolationDataset; -import pulse.input.InterpolationDataset.StandartType; +import pulse.input.listeners.ExternalDatasetListener; import pulse.math.Segment; import pulse.math.transforms.StickTransform; import pulse.problem.statements.Pulse2D; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; +import pulse.tasks.TaskManager; import pulse.util.PropertyHolder; public class ThermalProperties extends PropertyHolder { + private static final long serialVersionUID = 1868313258119863995L; private double a; private double l; private double Bi; @@ -56,7 +55,6 @@ public ThermalProperties() { signalHeight = (double) def(MAXTEMP).getValue(); T = (double) def(TEST_TEMPERATURE).getValue(); emissivity = (double) def(EMISSIVITY).getValue(); - initListeners(); fill(); } @@ -68,7 +66,6 @@ public ThermalProperties(ThermalProperties p) { this.T = p.T; this.signalHeight = p.signalHeight; this.emissivity = p.emissivity; - initListeners(); fill(); } @@ -78,17 +75,6 @@ public List findMalformedProperties() { return list; } - private void fill() { - var rhoCurve = getDataset(StandartType.DENSITY); - var cpCurve = getDataset(StandartType.HEAT_CAPACITY); - if (rhoCurve != null) { - rho = rhoCurve.interpolateAt(T); - } - if (cpCurve != null) { - cP = cpCurve.interpolateAt(T); - } - } - /** * Calculates some or all of the following properties: * Cp, ρ, &labmda;, @@ -99,21 +85,31 @@ private void fill() { * {@code TaskManager}. *

*/ - private void initListeners() { + + private void fill() { + var i = TaskManager.getManagerInstance(); + var rhoCurve = i.getDensityDataset(); + var cpCurve = i.getSpecificHeatDataset(); + if (rhoCurve != null) { + rho = rhoCurve.interpolateAt(T); + } + if (cpCurve != null) { + cP = cpCurve.interpolateAt(T); + } - InterpolationDataset.addListener(e -> { - if (getParent() == null) { - return; + i.addExternalDatasetListener(new ExternalDatasetListener() { + @Override + public void onSpecificHeatDataLoaded() { + cP = i.getSpecificHeatDataset().interpolateAt(T); } - if (e == StandartType.DENSITY) { - rho = getDataset(StandartType.DENSITY).interpolateAt(T); - } else if (e == StandartType.HEAT_CAPACITY) { - cP = getDataset(StandartType.HEAT_CAPACITY).interpolateAt(T); + @Override + public void onDensityDataLoaded() { + rho = i.getDensityDataset().interpolateAt(T); } - + }); - + } public ThermalProperties copy() { @@ -170,7 +166,7 @@ public void set(NumericPropertyKeyword type, NumericProperty value) { public void setHeatLoss(NumericProperty Bi) { requireType(Bi, HEAT_LOSS); this.Bi = (double) Bi.getValue(); - if(areThermalPropertiesLoaded()) { + if (areThermalPropertiesLoaded()) { calculateEmissivity(); } firePropertyChanged(this, Bi); @@ -248,13 +244,14 @@ public void setTestTemperature(NumericProperty T) { requireType(T, TEST_TEMPERATURE); this.T = (double) T.getValue(); - var heatCapacity = getDataset(HEAT_CAPACITY); + var i = TaskManager.getManagerInstance(); + var heatCapacity = i.getSpecificHeatDataset(); if (heatCapacity != null) { cP = heatCapacity.interpolateAt(this.T); } - var density = getDataset(StandartType.DENSITY); + var density = i.getDensityDataset(); if (density != null) { rho = density.interpolateAt(this.T); @@ -291,31 +288,31 @@ public NumericProperty getThermalConductivity() { public void calculateEmissivity() { double newEmissivity = Bi * thermalConductivity() / (4. * Math.pow(T, 3) * l * STEFAN_BOTLZMAN); var transform = new StickTransform(Segment.boundsFrom(EMISSIVITY)); - setEmissivity(derive(EMISSIVITY, + setEmissivity(derive(EMISSIVITY, transform.transform(newEmissivity)) ); } - + /** * Calculates the radiative Biot number. + * * @return the radiative Biot number. */ - public double radiationBiot() { double lambda = thermalConductivity(); return 4.0 * emissivity * STEFAN_BOTLZMAN * Math.pow(T, 3) * l / lambda; } - + /** - * Calculates the maximum Biot number at these conditions, which - * corresponds to an emissivity of unity. If emissivity is non-positive, - * returns the maximum Biot number defined in the XML file. + * Calculates the maximum Biot number at these conditions, which corresponds + * to an emissivity of unity. If emissivity is non-positive, returns the + * maximum Biot number defined in the XML file. + * * @return the maximum Biot number */ - public double maxRadiationBiot() { double absMax = Segment.boundsFrom(HEAT_LOSS).getMaximum(); - return emissivity > 0 ? radiationBiot() / emissivity : absMax; + return emissivity > 0 ? radiationBiot() / emissivity : absMax; } /** @@ -325,10 +322,10 @@ public double maxRadiationBiot() { * * @return the time factor */ - public double timeFactor() { + public double characteristicTime() { return l * l / a; } - + public double getThermalMass() { return cP * rho; } @@ -357,7 +354,7 @@ public final boolean areThermalPropertiesLoaded() { public double maximumHeating(Pulse2D pulse) { final double Q = (double) pulse.getLaserEnergy().getValue(); final double dLas = (double) pulse.getSpotDiameter().getValue(); - return 4.0 * emissivity * Q / (PI * dLas * dLas * l * getThermalMass() ); + return 4.0 * emissivity * Q / (PI * dLas * dLas * l * getThermalMass()); } public NumericProperty getEmissivity() { @@ -385,4 +382,4 @@ public String toString() { return sb.toString(); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/problem/statements/model/ThermoOpticalProperties.java b/src/main/java/pulse/problem/statements/model/ThermoOpticalProperties.java index 371fadc..ff844eb 100644 --- a/src/main/java/pulse/problem/statements/model/ThermoOpticalProperties.java +++ b/src/main/java/pulse/problem/statements/model/ThermoOpticalProperties.java @@ -25,6 +25,7 @@ public class ThermoOpticalProperties extends ThermalProperties implements Optimisable { + private static final long serialVersionUID = 3573682830421468534L; private double opticalThickness; private double planckNumber; private double scatteringAlbedo; @@ -33,29 +34,29 @@ public class ThermoOpticalProperties extends ThermalProperties implements Optimi public ThermoOpticalProperties() { super(); - this.opticalThickness = (double) def(OPTICAL_THICKNESS).getValue(); - this.planckNumber = (double) def(PLANCK_NUMBER).getValue(); - scatteringAnisotropy = (double) def(SCATTERING_ANISOTROPY).getValue(); - scatteringAlbedo = (double) def(SCATTERING_ALBEDO).getValue(); - convectiveLosses = (double) def(HEAT_LOSS_CONVECTIVE).getValue(); + this.opticalThickness = (double) def(OPTICAL_THICKNESS).getValue(); + this.planckNumber = (double) def(PLANCK_NUMBER).getValue(); + scatteringAnisotropy = (double) def(SCATTERING_ANISOTROPY).getValue(); + scatteringAlbedo = (double) def(SCATTERING_ALBEDO).getValue(); + convectiveLosses = (double) def(HEAT_LOSS_CONVECTIVE).getValue(); } public ThermoOpticalProperties(ThermalProperties p) { super(p); - opticalThickness = (double) def(OPTICAL_THICKNESS).getValue(); - planckNumber = (double) def(PLANCK_NUMBER).getValue(); - scatteringAlbedo = (double) def(SCATTERING_ALBEDO).getValue(); - scatteringAnisotropy = (double) def(SCATTERING_ANISOTROPY).getValue(); - convectiveLosses = (double) def(HEAT_LOSS_CONVECTIVE).getValue(); + opticalThickness = (double) def(OPTICAL_THICKNESS).getValue(); + planckNumber = (double) def(PLANCK_NUMBER).getValue(); + scatteringAlbedo = (double) def(SCATTERING_ALBEDO).getValue(); + scatteringAnisotropy = (double) def(SCATTERING_ANISOTROPY).getValue(); + convectiveLosses = (double) def(HEAT_LOSS_CONVECTIVE).getValue(); } public ThermoOpticalProperties(ThermoOpticalProperties p) { super(p); - this.opticalThickness = p.opticalThickness; - this.planckNumber = p.planckNumber; - this.scatteringAlbedo = p.scatteringAlbedo; - this.scatteringAnisotropy = p.scatteringAnisotropy; - this.convectiveLosses = p.convectiveLosses; + this.opticalThickness = p.opticalThickness; + this.planckNumber = p.planckNumber; + this.scatteringAlbedo = p.scatteringAlbedo; + this.scatteringAnisotropy = p.scatteringAnisotropy; + this.convectiveLosses = p.convectiveLosses; } @Override @@ -135,7 +136,7 @@ public void setScatteringAnisotropy(NumericProperty A1) { this.scatteringAnisotropy = (double) A1.getValue(); firePropertyChanged(this, A1); } - + public void setConvectiveLosses(NumericProperty losses) { requireType(losses, HEAT_LOSS_CONVECTIVE); this.convectiveLosses = (double) losses.getValue(); @@ -145,7 +146,7 @@ public void setConvectiveLosses(NumericProperty losses) { public NumericProperty getConvectiveLosses() { return derive(HEAT_LOSS_CONVECTIVE, convectiveLosses); } - + public NumericProperty getScatteringAlbedo() { return derive(SCATTERING_ALBEDO, scatteringAlbedo); } @@ -173,7 +174,7 @@ public void useTheoreticalEstimates(ExperimentalData c) { public String getDescriptor() { return "Thermo-Physical & Optical Properties"; } - + @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); @@ -186,7 +187,7 @@ public String toString() { sb.append(String.format("%n %-25s", this.getDensity())); return sb.toString(); } - + @Override public void optimisationVector(ParameterVector output) { Segment bounds = null; @@ -220,9 +221,9 @@ public void optimisationVector(ParameterVector output) { bounds = Segment.boundsFrom(HEAT_LOSS_CONVECTIVE); break; case HEAT_LOSS: - value = (double) getHeatLoss().getValue(); - bounds = new Segment(0.0, maxRadiationBiot() ); - break; + value = (double) getHeatLoss().getValue(); + bounds = new Segment(0.0, maxRadiationBiot()); + break; default: continue; diff --git a/src/main/java/pulse/problem/statements/model/TwoTemperatureProperties.java b/src/main/java/pulse/problem/statements/model/TwoTemperatureProperties.java index c879327..099fde4 100644 --- a/src/main/java/pulse/problem/statements/model/TwoTemperatureProperties.java +++ b/src/main/java/pulse/problem/statements/model/TwoTemperatureProperties.java @@ -11,10 +11,11 @@ public class TwoTemperatureProperties extends ThermalProperties { + private static final long serialVersionUID = 4157382023954200467L; private double exchangeSolid; private double exchangeGas; private double gasHeatLoss; - + public TwoTemperatureProperties() { super(); exchangeSolid = (double) def(SOLID_EXCHANGE_COEFFICIENT).getValue(); @@ -29,8 +30,7 @@ public TwoTemperatureProperties(ThermalProperties p) { this.exchangeSolid = np.exchangeSolid; this.exchangeGas = np.exchangeGas; this.gasHeatLoss = np.gasHeatLoss; - } - else { + } else { exchangeSolid = (double) def(SOLID_EXCHANGE_COEFFICIENT).getValue(); exchangeGas = (double) def(GAS_EXCHANGE_COEFFICIENT).getValue(); gasHeatLoss = (double) def(HEAT_LOSS_GAS).getValue(); @@ -80,7 +80,7 @@ public Set listedKeywords() { public NumericProperty getSolidExchangeCoefficient() { return derive(SOLID_EXCHANGE_COEFFICIENT, exchangeSolid); } - + public NumericProperty getGasExchangeCoefficient() { return derive(GAS_EXCHANGE_COEFFICIENT, exchangeGas); } @@ -90,7 +90,7 @@ public void setSolidExchangeCoefficient(NumericProperty p) { this.exchangeSolid = (double) p.getValue(); firePropertyChanged(this, p); } - + public void setGasExchangeCoefficient(NumericProperty p) { NumericProperty.requireType(p, GAS_EXCHANGE_COEFFICIENT); this.exchangeGas = (double) p.getValue(); diff --git a/src/main/java/pulse/properties/Flag.java b/src/main/java/pulse/properties/Flag.java index 94b6ce3..d6c6f10 100644 --- a/src/main/java/pulse/properties/Flag.java +++ b/src/main/java/pulse/properties/Flag.java @@ -12,6 +12,7 @@ */ public class Flag implements Property { + private static final long serialVersionUID = 4927536419752406797L; private NumericPropertyKeyword index; private boolean value; private String descriptor; @@ -26,15 +27,15 @@ public class Flag implements Property { public Flag(NumericPropertyKeyword type) { this(type, false); } - + public Flag(Flag f) { this(f.index, f.value); } - + public Flag(NumericPropertyKeyword type, boolean flag) { this.index = type; this.value = flag; - } + } /** * Creates a {@code Flag} with the following pre-specified parameters: type diff --git a/src/main/java/pulse/properties/NumericProperties.java b/src/main/java/pulse/properties/NumericProperties.java index cd5279e..3f0f5ff 100644 --- a/src/main/java/pulse/properties/NumericProperties.java +++ b/src/main/java/pulse/properties/NumericProperties.java @@ -1,11 +1,8 @@ package pulse.properties; -import java.text.DecimalFormat; -import java.text.NumberFormat; import java.util.List; import pulse.io.export.XMLConverter; -import pulse.ui.Messages; /** * Default operations with NumericProperties @@ -44,8 +41,8 @@ public static boolean isValueSensible(NumericProperty property, Number val) { double v = val.doubleValue(); final double EPS = 1E-12; boolean ok = true; - - if( !Double.isFinite(v) + + if (!Double.isFinite(v) || v > property.getMaximum().doubleValue() + EPS || v < property.getMinimum().doubleValue() - EPS) { ok = false; @@ -59,9 +56,9 @@ public static String printRangeAndNumber(NumericProperty p, Number value) { msg.append("Acceptable region for "); msg.append("parameter : "); msg.append(p.getValue().getClass().getSimpleName()); - msg.append(" [ " + p.getMinimum()); - msg.append(" : " + p.getMaximum() + " ]. "); - msg.append("Value received: " + value); + msg.append(" [ ").append(p.getMinimum()); + msg.append(" : ").append(p.getMaximum()).append(" ]. "); + msg.append("Value received: ").append(value); return msg.toString(); } @@ -94,7 +91,7 @@ public static int compare(NumericProperty a, NumericProperty b) { Double d1 = ((Number) a.getValue()).doubleValue(); Double d2 = ((Number) b.getValue()).doubleValue(); - final double eps = 1E-8 * (d1 + d2) / 2.0; + final double eps = 1E-8 * Math.abs(d1 + d2) / 2.0; return Math.abs(d1 - d2) < eps ? 0 : d1.compareTo(d2); } @@ -102,8 +99,7 @@ public static int compare(NumericProperty a, NumericProperty b) { /** * Searches for the default {@code NumericProperty} corresponding to * {@code keyword} in the list of pre-defined properties loaded from the - * respective {@code .xml} file, and if found creates a new - * { + * respective {@code .xml} file, and if found creates a new { * * @NumericProperty} which will replicate all field of the latter, but will * set its value to {@code value}. diff --git a/src/main/java/pulse/properties/NumericProperty.java b/src/main/java/pulse/properties/NumericProperty.java index 734a70f..495d1a1 100644 --- a/src/main/java/pulse/properties/NumericProperty.java +++ b/src/main/java/pulse/properties/NumericProperty.java @@ -22,6 +22,11 @@ */ public class NumericProperty implements Property, Comparable { + /** + * + */ + private static final long serialVersionUID = -7132274623596750984L; + private Number value; private Number minimum; @@ -263,6 +268,7 @@ public boolean equals(Object o) { @Override public int compareTo(NumericProperty arg0) { final int result = this.getType().compareTo(arg0.getType()); + int res = compare(this, arg0); return result != 0 ? result : compare(this, arg0); } @@ -310,34 +316,35 @@ public void setDefaultSearchVariable(boolean defaultSearchVariable) { public void setOptimisable(boolean optimisable) { this.optimisable = optimisable; } - + public Number getDimensionDelta() { - if(type == NumericPropertyKeyword.TEST_TEMPERATURE) + if (type == NumericPropertyKeyword.TEST_TEMPERATURE) { return -273.15; - else + } else { return 0.0; + } } - + /** - * Represents the bounds specified for this numeric property - * as a {@code Segment} object. The bound numbers are taken by - * their double values and assigned to the segment. + * Represents the bounds specified for this numeric property as a + * {@code Segment} object. The bound numbers are taken by their double + * values and assigned to the segment. + * * @return the bounds in which this property is allowed to change */ - public Segment getBounds() { return new Segment(minimum.doubleValue(), maximum.doubleValue()); } - + /** * Uses a {@code NumericPropertyFormatter} to generate a formatted output - * @return a formatted string output with the value (and error -- if available) - * of this numeric property + * + * @return a formatted string output with the value (and error -- if + * available) of this numeric property */ - public String formattedOutput() { var num = new NumericPropertyFormatter(this, true, true); return num.numberFormat(this).format(value); } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/properties/NumericPropertyFormatter.java b/src/main/java/pulse/properties/NumericPropertyFormatter.java index f3c83cf..065f060 100644 --- a/src/main/java/pulse/properties/NumericPropertyFormatter.java +++ b/src/main/java/pulse/properties/NumericPropertyFormatter.java @@ -30,6 +30,7 @@ */ public class NumericPropertyFormatter extends AbstractFormatter { + private static final long serialVersionUID = -7733589481239097566L; private NumericPropertyKeyword key; private Segment bounds; private boolean convertDimension = true; @@ -74,9 +75,9 @@ public NumberFormat numberFormat(NumericProperty p) { : (double) value; double absAdjustedValue = Math.abs(adjustedValue); - if (addHtmlTags && - ( (absAdjustedValue > UPPER_LIMIT) - || (absAdjustedValue < LOWER_LIMIT && absAdjustedValue > ZERO)) ) { + if (addHtmlTags + && ((absAdjustedValue > UPPER_LIMIT) + || (absAdjustedValue < LOWER_LIMIT && absAdjustedValue > ZERO))) { //format with scientific notations f = new ScientificFormat(p.getDimensionFactor(), p.getDimensionDelta()); } else { @@ -85,7 +86,7 @@ public NumberFormat numberFormat(NumericProperty p) { } } - + return f; } diff --git a/src/main/java/pulse/properties/NumericPropertyKeyword.java b/src/main/java/pulse/properties/NumericPropertyKeyword.java index a657171..344bdbe 100644 --- a/src/main/java/pulse/properties/NumericPropertyKeyword.java +++ b/src/main/java/pulse/properties/NumericPropertyKeyword.java @@ -157,13 +157,10 @@ public enum NumericPropertyKeyword { * sample (1D and 2D problems). */ HEAT_LOSS, - /** * The convective heat loss in diathermic and participating medium problems. */ - HEAT_LOSS_CONVECTIVE, - /** * A directive for the optimiser to maintain equal heat losses on all * surfaces of the sample. Note that the dimensionless heat losses, i.e. @@ -224,7 +221,6 @@ public enum NumericPropertyKeyword { * Statistical significance for calculating the critical value. */ SIGNIFICANCE, - /** * Optimiser statistic (usually, RSS). */ @@ -354,44 +350,36 @@ public enum NumericPropertyKeyword { * damping. A value of 1 gives pure Marquardt damping. */ DAMPING_RATIO, - /** * Determines how much weight is attributed to the front-face light source - * compared to rear face. Can be a number between zero and unity. + * compared to rear face. Can be a number between zero and unity. */ - SOURCE_GEOMETRIC_FACTOR, - /** * Max. no. of high-frequency waves in the sinusoidal baseline. */ - MAX_HIGH_FREQ_WAVES, - /** - * Max. no. of low-frequency waves in the sinusoidal baseline. - */ - + * Max. no. of low-frequency waves in the sinusoidal baseline. + */ MAX_LOW_FREQ_WAVES, - /** * Energy exchange coefficient in the two-temperature model (g). */ - SOLID_EXCHANGE_COEFFICIENT, - /** * Energy exchange coefficient in the two-temperature model (g'). */ - GAS_EXCHANGE_COEFFICIENT, - /** * Heat loss for the gas in the 2T-model. */ - - HEAT_LOSS_GAS; - + HEAT_LOSS_GAS, + /** + * Value of objective function. + */ + OBJECTIVE_FUNCTION; + public static Optional findAny(String key) { return Arrays.asList(values()).stream().filter(keys -> keys.toString().equalsIgnoreCase(key)).findAny(); } diff --git a/src/main/java/pulse/properties/Property.java b/src/main/java/pulse/properties/Property.java index a1215d7..0ff7804 100644 --- a/src/main/java/pulse/properties/Property.java +++ b/src/main/java/pulse/properties/Property.java @@ -1,10 +1,12 @@ package pulse.properties; +import java.io.Serializable; + /** * The basic interface for properties. The only declared functionality consists * in the ability to report the associated value and deliver text description. */ -public interface Property { +public interface Property extends Serializable { /** * Retrieves the value of this {@code Property}. diff --git a/src/main/java/pulse/properties/SampleName.java b/src/main/java/pulse/properties/SampleName.java index 5ef7419..0e355f2 100644 --- a/src/main/java/pulse/properties/SampleName.java +++ b/src/main/java/pulse/properties/SampleName.java @@ -4,10 +4,11 @@ public class SampleName implements Property { + private static final long serialVersionUID = -965821128124753850L; private String name; - public SampleName() { - //null name + public SampleName(String name) { + this.name = name; } @Override @@ -41,22 +42,28 @@ public String toString() { } @Override - public boolean equals(Object o) { - if (o == this) { + public int hashCode() { + int hash = 5; + hash = 43 * hash + Objects.hashCode(this.name); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { return true; } - - if (o == null) { + if (obj == null) { return false; } - - boolean result = false; - - if (o instanceof SampleName) { - result = name.equals(((SampleName) o).getValue()); + if (getClass() != obj.getClass()) { + return false; } - - return result; + final SampleName other = (SampleName) obj; + if (!Objects.equals(this.name, other.name)) { + return false; + } + return true; } } diff --git a/src/main/java/pulse/properties/ScientificFormat.java b/src/main/java/pulse/properties/ScientificFormat.java index cd9baca..a61108c 100644 --- a/src/main/java/pulse/properties/ScientificFormat.java +++ b/src/main/java/pulse/properties/ScientificFormat.java @@ -30,6 +30,7 @@ */ public class ScientificFormat extends NumberFormat { + private static final long serialVersionUID = -6509402151736747913L; private final int dimensionFactor; private final double dimensionDelta; diff --git a/src/main/java/pulse/search/GeneralTask.java b/src/main/java/pulse/search/GeneralTask.java index 44c64ac..426b067 100644 --- a/src/main/java/pulse/search/GeneralTask.java +++ b/src/main/java/pulse/search/GeneralTask.java @@ -15,41 +15,41 @@ import static pulse.tasks.processing.Buffer.getSize; import pulse.util.Accessible; -public abstract class GeneralTask +public abstract class GeneralTask extends Accessible implements Runnable { private IterativeState path; //current sate private IterativeState best; //best state - + private final Buffer buffer; private PathOptimiser optimiser; - + public GeneralTask() { buffer = new Buffer(); buffer.setParent(this); } - + public abstract List activeParameters(); /** - * Creates a search vector populated by parameters that - * are included in the optimisation routine. + * Creates a search vector populated by parameters that are included in the + * optimisation routine. + * * @return the parameter vector with optimisation parameters */ - public abstract ParameterVector searchVector(); - + /** - * Tries to assign a selected set of parameters to the search vector - * used in optimisation. - * @param pv a parameter vector containing all of the optimisation parameters - * whose values will be assigned to this task - * @throws SolverException + * Tries to assign a selected set of parameters to the search vector used in + * optimisation. + * + * @param pv a parameter vector containing all of the optimisation + * parameters whose values will be assigned to this task + * @throws SolverException */ - public abstract void assign(ParameterVector pv) throws SolverException; - - /** + + /** *

* Runs this task if is either {@code READY} or {@code QUEUED}. Otherwise, * will do nothing. After making some preparatory steps, will initiate a @@ -64,9 +64,10 @@ public GeneralTask() { @Override public void run() { setDefaultOptimiser(); - setIterativeState( optimiser.initState(this) ); + best = null; + setIterativeState(optimiser.initState(this)); - var errorTolerance = (double) optimiser.getErrorTolerance().getValue(); + double errorTolerance = (double) optimiser.getErrorTolerance().getValue(); int bufferSize = (Integer) getSize().getValue(); buffer.init(); //correlationBuffer.clear(); @@ -77,7 +78,7 @@ public void run() { var singleThreadExecutor = Executors.newSingleThreadExecutor(); var response = getResponse(); - + try { response.objectiveFunction(this); } catch (SolverException e1) { @@ -99,7 +100,7 @@ public void run() { onSolverException(e); break outer; } - + //if global best is better than the converged value if (best != null && best.getCost() < path.getCost()) { try { @@ -113,7 +114,7 @@ public void run() { } final var j = i; - + bufferFutures.add(CompletableFuture.runAsync(() -> { buffer.fill(this, j); intermediateProcessing(); @@ -122,52 +123,50 @@ public void run() { } bufferFutures.forEach(future -> future.join()); - - } while (buffer.isErrorTooHigh(errorTolerance) + + } while (buffer.isErrorTooHigh(errorTolerance) && isInProgress()); singleThreadExecutor.shutdown(); if (isInProgress()) { postProcessing(); - } + } - } + } + + public abstract boolean isInProgress(); - public abstract boolean isInProgress(); - /** - * Override this to add intermediate processing of results e.g. - * with a correlation test. + * Override this to add intermediate processing of results e.g. with a + * correlation test. */ - public void intermediateProcessing() { //empty } - + /** * Specifies what should be done when a solver exception is encountered. * Empty by default + * * @param e1 a solver exception */ - public void onSolverException(SolverException e1) { //empty } - + /** - * Override this to add post-processing checks - * e.g. normality tests or range checking. + * Override this to add post-processing checks e.g. normality tests or range + * checking. */ - public void postProcessing() { //empty } - + public final Buffer getBuffer() { return buffer; } - + public void setIterativeState(IterativeState state) { this.path = state; } @@ -195,23 +194,24 @@ public void storeState() { best = new IterativeState(path); } } - + public final void setOptimiser(PathOptimiser optimiser) { this.optimiser = optimiser; } - + public void setDefaultOptimiser() { var instance = PathOptimiser.getInstance(); - if(optimiser == null || optimiser != instance) { + if (optimiser == null || optimiser != instance) { setOptimiser(PathOptimiser.getInstance()); } } - + public double objectiveFunction() throws SolverException { return getResponse().objectiveFunction(this); } - - public abstract I getInput(); + + public abstract I getInput(); + public abstract R getResponse(); - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/search/Optimisable.java b/src/main/java/pulse/search/Optimisable.java index 44666f5..53c29d4 100644 --- a/src/main/java/pulse/search/Optimisable.java +++ b/src/main/java/pulse/search/Optimisable.java @@ -1,6 +1,5 @@ package pulse.search; - import pulse.math.ParameterVector; import pulse.problem.schemes.solvers.SolverException; diff --git a/src/main/java/pulse/search/SimpleOptimisationTask.java b/src/main/java/pulse/search/SimpleOptimisationTask.java index 4fbce66..ec0fd4e 100644 --- a/src/main/java/pulse/search/SimpleOptimisationTask.java +++ b/src/main/java/pulse/search/SimpleOptimisationTask.java @@ -30,11 +30,11 @@ public SimpleOptimisationTask(T optimisable, DiscreteInput input) { this.input = input; this.optimisable = optimisable; } - + @Override public void run() { var optimiser = PathOptimiser.getInstance(); - if(optimiser == null) { + if (optimiser == null) { PathOptimiser.setInstance(LMOptimiser.getInstance()); } super.run(); @@ -74,20 +74,20 @@ public boolean isInProgress() { public void set(NumericPropertyKeyword type, NumericProperty property) { optimisable.set(type, property); } - + @Override - public List activeParameters() { + public List activeParameters() { return selectActiveAndListed(ActiveFlags.getAllFlags(), optimisable); } - + @Override public void setDefaultOptimiser() { setOptimiser(LMOptimiser.getInstance()); } - + @Override public DiscreteInput getInput() { return input; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/search/direction/ActiveFlags.java b/src/main/java/pulse/search/direction/ActiveFlags.java index 81103d1..1ad72f3 100644 --- a/src/main/java/pulse/search/direction/ActiveFlags.java +++ b/src/main/java/pulse/search/direction/ActiveFlags.java @@ -1,5 +1,6 @@ package pulse.search.direction; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -13,8 +14,9 @@ import pulse.tasks.TaskManager; import pulse.util.PropertyHolder; -public class ActiveFlags { +public class ActiveFlags implements Serializable { + private static final long serialVersionUID = -8711073682010113698L; private static List flags; static { @@ -42,12 +44,12 @@ public static Set availableProperties() { return set; } - var p = ( (Calculation) t.getResponse() ).getProblem(); + var p = ((Calculation) t.getResponse()).getProblem(); if (p != null) { var fullList = p.listedKeywords(); - fullList.addAll( ( (ExperimentalData) t.getInput() ).listedKeywords()); + fullList.addAll(((ExperimentalData) t.getInput()).listedKeywords()); NumericPropertyKeyword key; for (Flag property : flags) { @@ -62,36 +64,36 @@ public static Set availableProperties() { return set; } - + public static Flag get(NumericPropertyKeyword key) { var flag = flags.stream().filter(f -> f.getType() == key).findAny(); return flag.isPresent() ? flag.get() : null; - } - + } + /** * Creates a deep copy of the flags collection. + * * @return a deep copy of the flags */ - public static List storeState() { var copy = new ArrayList(); - for(Flag f : flags) { + for (Flag f : flags) { copy.add(new Flag(f)); } return copy; } - + /** - * Loads the argument into the current list of flags. - * This will update any matching flags and assign values correpon - * @param flags + * Loads the argument into the current list of flags. This will update any + * matching flags and assign values correpon + * + * @param flags */ - public static void loadState(List flags) { - for(Flag f : ActiveFlags.flags) { - Optional existingFlag = flags.stream().filter(fl -> - fl.getType() == f.getType()).findFirst(); - if(existingFlag.isPresent()) { + for (Flag f : ActiveFlags.flags) { + Optional existingFlag = flags.stream().filter(fl + -> fl.getType() == f.getType()).findFirst(); + if (existingFlag.isPresent()) { f.setValue((boolean) existingFlag.get().getValue()); } } @@ -99,15 +101,15 @@ public static void loadState(List flags) { public static List selectActiveAndListed(List flags, PropertyHolder listed) { //return empty list - if(listed == null) { + if (listed == null) { return new ArrayList<>(); } - + return selectActiveTypes(flags).stream() .filter(type -> listed.isListedNumericType(type)) .collect(Collectors.toList()); } - + public static List selectActiveTypes(List flags) { return Flag.selectActive(flags).stream().map(flag -> flag.getType()).collect(Collectors.toList()); } diff --git a/src/main/java/pulse/search/direction/BFGSOptimiser.java b/src/main/java/pulse/search/direction/BFGSOptimiser.java index 273b936..8911926 100644 --- a/src/main/java/pulse/search/direction/BFGSOptimiser.java +++ b/src/main/java/pulse/search/direction/BFGSOptimiser.java @@ -30,6 +30,10 @@ */ public class BFGSOptimiser extends CompositePathOptimiser { + /** + * + */ + private static final long serialVersionUID = -8542438015176648987L; private static BFGSOptimiser instance = new BFGSOptimiser(); private BFGSOptimiser() { @@ -68,9 +72,9 @@ public void prepare(GeneralTask task) throws SolverException { p.setHessian(hessian); // g_k, g_k+1, p_k+1, B_k, alpha_k+1 p.setGradient(g1); // set g1 as the new gradient for next step } - + /** - * Uses the BFGS formula to calculate the Hessian. + * Uses the BFGS formula to calculate the Hessian. * * @param g1 gradient at step k * @param g2 gradient at step k+1 diff --git a/src/main/java/pulse/search/direction/ComplexPath.java b/src/main/java/pulse/search/direction/ComplexPath.java index e6fc8a3..8340c81 100644 --- a/src/main/java/pulse/search/direction/ComplexPath.java +++ b/src/main/java/pulse/search/direction/ComplexPath.java @@ -4,7 +4,6 @@ import pulse.math.linear.SquareMatrix; import pulse.search.GeneralTask; -import pulse.tasks.SearchTask; /** *

@@ -16,6 +15,7 @@ */ public class ComplexPath extends GradientGuidedPath { + private static final long serialVersionUID = -1520823504831702183L; private SquareMatrix hessian; private SquareMatrix inverseHessian; diff --git a/src/main/java/pulse/search/direction/CompositePathOptimiser.java b/src/main/java/pulse/search/direction/CompositePathOptimiser.java index 7976646..1e91de6 100644 --- a/src/main/java/pulse/search/direction/CompositePathOptimiser.java +++ b/src/main/java/pulse/search/direction/CompositePathOptimiser.java @@ -16,23 +16,22 @@ public abstract class CompositePathOptimiser extends GradientBasedOptimiser { - private InstanceDescriptor instanceDescriptor + private InstanceDescriptor instanceDescriptor = new InstanceDescriptor<>( - "Linear Optimiser Selector", LinearOptimiser.class); + "Linear Optimiser Selector", LinearOptimiser.class); private LinearOptimiser linearSolver; - - /** - * Maximum number of consequent failed iterations that can be rejected. - * Up to {@value MAX_FAILED_ATTEMPTS} failed attempts are allowed. + + /** + * Maximum number of consequent failed iterations that can be rejected. Up + * to {@value MAX_FAILED_ATTEMPTS} failed attempts are allowed. */ - public final static int MAX_FAILED_ATTEMPTS = 2; - + /** * For numerical comparison. */ - public final static double EPS = 1e-10; + public final static double EPS = 1e-10; public CompositePathOptimiser() { instanceDescriptor.setSelectedDescriptor(WolfeOptimiser.class.getSimpleName()); @@ -48,7 +47,7 @@ private void initLinearOptimiser() { @Override public boolean iteration(GeneralTask task) throws SolverException { var p = (GradientGuidedPath) task.getIterativeState(); // the previous state of the task - + boolean accept = true; /* @@ -61,7 +60,8 @@ public boolean iteration(GeneralTask task) throws SolverException { } else { double initialCost = task.getResponse().objectiveFunction(task); - var parameters = task.searchVector(); + p.setCost(initialCost); + var parameters = task.searchVector(); p.setParameters(parameters); // store current parameters @@ -70,32 +70,33 @@ public boolean iteration(GeneralTask task) throws SolverException { p.setLinearStep(step); // new set of parameters determined through search - var candidateParams = parameters.toVector().sum(dir.multiply(step)); + var candidateParams = parameters.toVector().sum(dir.multiply(step)); var candidateVector = new ParameterVector(parameters, candidateParams); - - if(candidateVector.findMalformedElements().isEmpty()) { + + if (candidateVector.findMalformedElements().isEmpty()) { task.assign(candidateVector); // assign new parameters } - - double newCost = task.getResponse().objectiveFunction(task); + + double newCost = task.getResponse().objectiveFunction(task); // calculate the sum of squared residuals - if (newCost > initialCost - EPS - && p.getFailedAttempts() < MAX_FAILED_ATTEMPTS - && p instanceof ComplexPath) { - var complexPath = (ComplexPath)p; + if (newCost > initialCost - EPS + && p.getFailedAttempts() < MAX_FAILED_ATTEMPTS + && p instanceof ComplexPath) { + var complexPath = (ComplexPath) p; task.assign(parameters); // roll back if cost increased // attempt to reset -> in case of Hessian-based methods, // this will change the Hessian) { - complexPath.setHessian( createIdentityMatrix(parameters.dimension()) ); + complexPath.setHessian(createIdentityMatrix(parameters.dimension())); p.incrementFailedAttempts(); accept = false; } else { task.storeState(); p.resetFailedAttempts(); this.prepare(task); // update gradients, Hessians, etc. -> for the next step, [k + 1] + p.setCost(newCost); p.incrementStep(); // increment the counter of successful steps - } + } } diff --git a/src/main/java/pulse/search/direction/DirectionSolver.java b/src/main/java/pulse/search/direction/DirectionSolver.java index c8e55a1..f96c191 100644 --- a/src/main/java/pulse/search/direction/DirectionSolver.java +++ b/src/main/java/pulse/search/direction/DirectionSolver.java @@ -1,9 +1,10 @@ package pulse.search.direction; +import java.io.Serializable; import pulse.math.linear.Vector; import pulse.problem.schemes.solvers.SolverException; -public interface DirectionSolver { +public interface DirectionSolver extends Serializable { /** * Finds the direction of the minimum using the previously calculated values diff --git a/src/main/java/pulse/search/direction/GradientBasedOptimiser.java b/src/main/java/pulse/search/direction/GradientBasedOptimiser.java index abee584..c55405e 100644 --- a/src/main/java/pulse/search/direction/GradientBasedOptimiser.java +++ b/src/main/java/pulse/search/direction/GradientBasedOptimiser.java @@ -82,7 +82,7 @@ public Vector gradient(GeneralTask task) throws SolverException { final var pVector = params.toVector(); var grad = new Vector(params.dimension()); final var ps = params.getParameters(); - + for (int i = 0, size = params.dimension(); i < size; i++) { var key = ps.get(i).getIdentifier().getKeyword(); var defProp = key != null ? NumericProperties.def(key) : null; diff --git a/src/main/java/pulse/search/direction/GradientGuidedPath.java b/src/main/java/pulse/search/direction/GradientGuidedPath.java index 7b9a06c..f4dd4f8 100644 --- a/src/main/java/pulse/search/direction/GradientGuidedPath.java +++ b/src/main/java/pulse/search/direction/GradientGuidedPath.java @@ -30,6 +30,10 @@ */ public class GradientGuidedPath extends IterativeState { + /** + * + */ + private static final long serialVersionUID = -6450999613326096767L; private Vector direction; private Vector gradient; private double minimumPoint; @@ -51,7 +55,7 @@ public void configure(GeneralTask t) { try { this.gradient = ((GradientBasedOptimiser) PathOptimiser.getInstance()).gradient(t); } catch (SolverException ex) { - t.onSolverException( new SolverException("Gradient calculation error", OPTIMISATION_ERROR)); + t.onSolverException(new SolverException("Gradient calculation error", OPTIMISATION_ERROR)); ex.printStackTrace(); } minimumPoint = 0.0; @@ -79,6 +83,6 @@ public double getMinimumPoint() { public void setLinearStep(double min) { minimumPoint = min; - } + } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/search/direction/IterativeState.java b/src/main/java/pulse/search/direction/IterativeState.java index 05fbaca..6d25807 100644 --- a/src/main/java/pulse/search/direction/IterativeState.java +++ b/src/main/java/pulse/search/direction/IterativeState.java @@ -1,5 +1,6 @@ package pulse.search.direction; +import java.io.Serializable; import pulse.math.ParameterVector; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericPropertyKeyword.ITERATION; @@ -7,40 +8,44 @@ import pulse.properties.NumericProperty; import pulse.search.GeneralTask; -public class IterativeState { +public class IterativeState implements Serializable { + private static final long serialVersionUID = -3924087865736298552L; private ParameterVector parameters; private double cost = Double.POSITIVE_INFINITY; private int iteration; private int failedAttempts; /** - * Stores the parameter vector and cost function value associated with the specified state. + * Stores the parameter vector and cost function value associated with the + * specified state. + * * @param other another state of the optimiser */ - public IterativeState(IterativeState other) { this.parameters = new ParameterVector(other.parameters); this.cost = other.cost; } - + public IterativeState(GeneralTask t) { this.parameters = t.searchVector(); } - + //default constructor - public IterativeState() {} - + public IterativeState() { + } + public double getCost() { return cost; } - + public void setCost(double cost) { this.cost = cost; } - + public void reset() { iteration = 0; + setCost(Double.POSITIVE_INFINITY); } public NumericProperty getIteration() { @@ -62,7 +67,7 @@ public void resetFailedAttempts() { public void incrementFailedAttempts() { failedAttempts++; } - + public ParameterVector getParameters() { return parameters; } @@ -71,4 +76,4 @@ public void setParameters(ParameterVector parameters) { this.parameters = parameters; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/search/direction/LMOptimiser.java b/src/main/java/pulse/search/direction/LMOptimiser.java index 4c3247a..9d4c1e5 100644 --- a/src/main/java/pulse/search/direction/LMOptimiser.java +++ b/src/main/java/pulse/search/direction/LMOptimiser.java @@ -36,14 +36,14 @@ */ public class LMOptimiser extends GradientBasedOptimiser { + private static final long serialVersionUID = -7954867240278082038L; private static final LMOptimiser instance = new LMOptimiser(); private double dampingRatio; - + /** * Up to {@value MAX_FAILED_ATTEMPTS} failed attempts are allowed. */ - - public final static int MAX_FAILED_ATTEMPTS = 4; + public final static int MAX_FAILED_ATTEMPTS = 5; private LMOptimiser() { super(); @@ -69,6 +69,7 @@ public boolean iteration(GeneralTask task) throws SolverException { } else { double initialCost = task.objectiveFunction(); + p.setCost(initialCost); var parameters = task.searchVector(); p.setParameters(parameters); // store current parameters @@ -78,15 +79,15 @@ public boolean iteration(GeneralTask task) throws SolverException { var lmDirection = getSolver().direction(p); var candidate = parameters.toVector().sum(lmDirection); - - if( Arrays.stream( candidate.getData() ).anyMatch(el -> !Double.isFinite(el) ) ) { + + if (Arrays.stream(candidate.getData()).anyMatch(el -> !Double.isFinite(el))) { throw new SolverException("Illegal candidate parameters: not finite! " + p.getIteration(), ILLEGAL_PARAMETERS); } - + task.assign(new ParameterVector( parameters, candidate)); // assign new parameters - + double newCost = task.objectiveFunction(); // calculate the sum of squared residuals /* @@ -103,6 +104,7 @@ public boolean iteration(GeneralTask task) throws SolverException { p.resetFailedAttempts(); p.setLambda(p.getLambda() / 3.0); p.setComputeJacobian(false); + p.setCost(newCost); p.incrementStep(); // increment the counter of successful steps } @@ -133,12 +135,12 @@ public void prepare(GeneralTask task) throws SolverException { // the Jacobian is then used to calculate the 'gradient' Vector g1 = halfGradient(p); // g1 p.setGradient(g1); - - if(Arrays.stream(g1.getData()).anyMatch(v -> !Double.isFinite(v))) { + + if (Arrays.stream(g1.getData()).anyMatch(v -> !Double.isFinite(v))) { throw new SolverException("Could not calculate objective function gradient", - OPTIMISATION_ERROR); + OPTIMISATION_ERROR); } - + // the Hessian is then regularised by adding labmda*I var hessian = p.getNonregularisedHessian(); var damping = (levenbergDamping(hessian).multiply(dampingRatio) @@ -173,7 +175,7 @@ public void prepare(GeneralTask task) throws SolverException { public RectangularMatrix jacobian(GeneralTask task) throws SolverException { var residualCalculator = task.getResponse().getOptimiserStatistic(); - + var p = ((LMPath) task.getIterativeState()); final var params = p.getParameters(); @@ -184,12 +186,12 @@ public RectangularMatrix jacobian(GeneralTask task) throws SolverException { var jacobian = new double[numPoints][numParams]; var ps = params.getParameters(); - + for (int i = 0; i < numParams; i++) { var key = ps.get(i).getIdentifier().getKeyword(); - double dx = dx( - key != null ? NumericProperties.def(key) : null, + double dx = dx( + key != null ? NumericProperties.def(key) : null, ps.get(i).inverseTransform()); final var shift = new Vector(numParams); @@ -199,19 +201,19 @@ public RectangularMatrix jacobian(GeneralTask task) throws SolverException { task.assign(new ParameterVector(params, pVector.sum(shift))); task.objectiveFunction(); var r = residualCalculator.getResiduals(); - - for (int j = 0, realNumPoints = Math.min(numPoints, r.size()); - j < realNumPoints; j++) { + + for (int j = 0, realNumPoints = Math.min(numPoints, r.size()); + j < realNumPoints; j++) { jacobian[j][i] = r.get(j) / dx; } - + // - shift task.assign(new ParameterVector(params, pVector.subtract(shift))); task.objectiveFunction(); - for (int j = 0, realNumPoints = Math.min(numPoints, r.size()); + for (int j = 0, realNumPoints = Math.min(numPoints, r.size()); j < realNumPoints; j++) { jacobian[j][i] -= r.get(j) / dx; @@ -219,14 +221,14 @@ public RectangularMatrix jacobian(GeneralTask task) throws SolverException { } } - + // revert to original params task.assign(params); return Matrices.createMatrix(jacobian); } - + @Override public GradientGuidedPath initState(GeneralTask t) { return new LMPath(t); @@ -258,8 +260,8 @@ private SquareMatrix levenbergDamping(SquareMatrix hessian) { private SquareMatrix marquardtDamping(SquareMatrix hessian) { return hessian.blockDiagonal(); } - - @Override + + @Override public Set listedKeywords() { var set = super.listedKeywords(); set.add(DAMPING_RATIO); diff --git a/src/main/java/pulse/search/direction/LMPath.java b/src/main/java/pulse/search/direction/LMPath.java index 2528968..39e9985 100644 --- a/src/main/java/pulse/search/direction/LMPath.java +++ b/src/main/java/pulse/search/direction/LMPath.java @@ -7,6 +7,7 @@ class LMPath extends ComplexPath { + private static final long serialVersionUID = -7154616034580697035L; private Vector residualVector; private RectangularMatrix jacobian; private SquareMatrix nonregularisedHessian; diff --git a/src/main/java/pulse/search/direction/PathOptimiser.java b/src/main/java/pulse/search/direction/PathOptimiser.java index cb5d4fa..21618a8 100644 --- a/src/main/java/pulse/search/direction/PathOptimiser.java +++ b/src/main/java/pulse/search/direction/PathOptimiser.java @@ -94,7 +94,7 @@ public void reset() { * @see pulse.search.linear.LinearOptimiser */ public abstract boolean iteration(GeneralTask task) throws SolverException; - + /** * Defines a set of procedures to be run at the end of the search iteration. * @@ -216,7 +216,7 @@ protected final void setSolver(DirectionSolver solver) { /** * Checks if this optimiser is compatible with the statistic passed to the * method as its argument.By default, this will accept any - {@code OptimiserStatistic} + * {@code OptimiserStatistic} * * @param os a selected optimiser metric * @return {@code true}, if not specified otherwise by its subclass diff --git a/src/main/java/pulse/search/direction/SR1Optimiser.java b/src/main/java/pulse/search/direction/SR1Optimiser.java index 8b2f708..716753f 100644 --- a/src/main/java/pulse/search/direction/SR1Optimiser.java +++ b/src/main/java/pulse/search/direction/SR1Optimiser.java @@ -13,6 +13,8 @@ public class SR1Optimiser extends CompositePathOptimiser { + private static final long serialVersionUID = -3041166132227281210L; + private static SR1Optimiser instance = new SR1Optimiser(); private final static double r = 1E-8; diff --git a/src/main/java/pulse/search/direction/SteepestDescentOptimiser.java b/src/main/java/pulse/search/direction/SteepestDescentOptimiser.java index 8fcbc31..ee9b68e 100644 --- a/src/main/java/pulse/search/direction/SteepestDescentOptimiser.java +++ b/src/main/java/pulse/search/direction/SteepestDescentOptimiser.java @@ -16,6 +16,10 @@ */ public class SteepestDescentOptimiser extends CompositePathOptimiser { + /** + * + */ + private static final long serialVersionUID = -6868259511333467862L; private static SteepestDescentOptimiser instance = new SteepestDescentOptimiser(); private SteepestDescentOptimiser() { @@ -67,4 +71,4 @@ public GradientGuidedPath initState(GeneralTask t) { return new GradientGuidedPath(t); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/search/direction/pso/ConstrictionMover.java b/src/main/java/pulse/search/direction/pso/ConstrictionMover.java index f19d824..db1d89b 100644 --- a/src/main/java/pulse/search/direction/pso/ConstrictionMover.java +++ b/src/main/java/pulse/search/direction/pso/ConstrictionMover.java @@ -4,16 +4,16 @@ import pulse.math.linear.Vector; public class ConstrictionMover implements Mover { - + private double c1; //social private double c2; //cognitive - private double chi; + private double chi; public final static double DEFAULT_CHI = 0.7298; public final static double DEFAULT_C = 1.49618; - + public ConstrictionMover() { - chi = DEFAULT_CHI; - c1 = c2 = DEFAULT_C; + chi = DEFAULT_CHI; + c1 = c2 = DEFAULT_C; } @Override @@ -23,24 +23,24 @@ public ParticleState attemptMove(Particle p, Particle[] neighbours, ParticleStat var curPosV = curPos.toVector(); final int n = curPos.dimension(); - Vector nsum = new Vector(n); + Vector nsum = new Vector(n); - var localBest = p.getBestState().getPosition(); //best position by local particle + var localBest = p.getBestState().getPosition(); //best position by local particle var localBestV = localBest.toVector(); var globalBest = gBest.getPosition(); //best position by any particle var globalBestV = globalBest.toVector(); - + nsum = nsum.sum(Vector.random(n, 0.0, c1) - .multComponents(localBestV.subtract(curPosV)) - ); - + .multComponents(localBestV.subtract(curPosV)) + ); + nsum = nsum.sum(Vector.random(n, 0.0, c2) - .multComponents(globalBestV.subtract(curPosV)) - ); + .multComponents(globalBestV.subtract(curPosV)) + ); var newVelocity = (current.getVelocity().toVector().sum(nsum)).multiply(chi); var newPosition = curPosV.sum(newVelocity); - + return new ParticleState( new ParameterVector(curPos, newPosition), new ParameterVector(curPos, newVelocity)); diff --git a/src/main/java/pulse/search/direction/pso/FIPSMover.java b/src/main/java/pulse/search/direction/pso/FIPSMover.java index b6869ec..09fe7a6 100644 --- a/src/main/java/pulse/search/direction/pso/FIPSMover.java +++ b/src/main/java/pulse/search/direction/pso/FIPSMover.java @@ -20,26 +20,26 @@ public ParticleState attemptMove(Particle p, Particle[] neighbours, ParticleStat var current = p.getCurrentState(); var curPos = current.getPosition(); var curPosV = curPos.toVector(); - + final int n = curPos.dimension(); final double nLength = (double) neighbours.length; - Vector nsum = new Vector(n); + Vector nsum = new Vector(n); for (var neighbour : neighbours) { var nBestPos = neighbour.getBestState().getPosition(); //best position ever achieved so far by the neighbour - nsum = nsum.sum(Vector.random(n, 0.0, phi/nLength) + nsum = nsum.sum(Vector.random(n, 0.0, phi / nLength) .multComponents(nBestPos.toVector().subtract(curPosV)) ); } var newVelocity = (current.getVelocity().toVector().sum(nsum)).multiply(chi); var newPosition = curPosV.sum(newVelocity); - + return new ParticleState( new ParameterVector(curPos, newPosition), new ParameterVector(curPos, newVelocity)); } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/search/direction/pso/Mover.java b/src/main/java/pulse/search/direction/pso/Mover.java index d1db1d0..1d7ecc2 100644 --- a/src/main/java/pulse/search/direction/pso/Mover.java +++ b/src/main/java/pulse/search/direction/pso/Mover.java @@ -4,4 +4,4 @@ public interface Mover { public ParticleState attemptMove(Particle p, Particle[] neighbours, ParticleState gBest); -} \ No newline at end of file +} diff --git a/src/main/java/pulse/search/direction/pso/ParticleState.java b/src/main/java/pulse/search/direction/pso/ParticleState.java index 68f68c1..01985e4 100644 --- a/src/main/java/pulse/search/direction/pso/ParticleState.java +++ b/src/main/java/pulse/search/direction/pso/ParticleState.java @@ -15,7 +15,7 @@ public ParticleState(ParameterVector cur) { //set initial velocity to zero velocity.setValues(new Vector(cur.dimension())); - + this.fitness = Double.MAX_VALUE; } @@ -41,7 +41,7 @@ public final void randomise(ParameterVector pos) { double max = p.getBounds().getMaximum(); return min + Math.random() * (max - min); }).toArray(); - + Vector randomVector = new Vector(randomValues); position.setValues(randomVector); } diff --git a/src/main/java/pulse/search/direction/pso/ParticleSwarmOptimiser.java b/src/main/java/pulse/search/direction/pso/ParticleSwarmOptimiser.java index 6f39f8b..f8cbf29 100644 --- a/src/main/java/pulse/search/direction/pso/ParticleSwarmOptimiser.java +++ b/src/main/java/pulse/search/direction/pso/ParticleSwarmOptimiser.java @@ -19,12 +19,12 @@ public ParticleSwarmOptimiser() { protected void moveParticles() { var topology = swarmState.getNeighborhoodTopology(); for (var p : swarmState.getParticles()) { - p.adopt(mover.attemptMove(p, - topology.neighbours(p, swarmState), + p.adopt(mover.attemptMove(p, + topology.neighbours(p, swarmState), swarmState.getBestSoFar())); var data = p.getCurrentState().getPosition().toVector().getData(); StringBuilder sb = new StringBuilder().append(p.getId()).append(" "); - for(var d : data) { + for (var d : data) { sb.append(d).append(" "); } System.err.println(sb.toString()); @@ -46,7 +46,8 @@ public boolean iteration(GeneralTask task) throws SolverException { swarmState.incrementStep(); task.assign(swarmState.getBestSoFar().getPosition()); - task.objectiveFunction(); + double cost = task.objectiveFunction(); + swarmState.setCost(cost); return true; } @@ -62,7 +63,7 @@ public IterativeState initState(GeneralTask t) { swarmState.create(); return swarmState; } - + //TODO @Override public boolean compatibleWith(OptimiserStatistic os) { diff --git a/src/main/java/pulse/search/direction/pso/StaticTopologies.java b/src/main/java/pulse/search/direction/pso/StaticTopologies.java index 8fa5c24..c466e8f 100644 --- a/src/main/java/pulse/search/direction/pso/StaticTopologies.java +++ b/src/main/java/pulse/search/direction/pso/StaticTopologies.java @@ -32,7 +32,7 @@ public class StaticTopologies { final int latticeParameter = (int) Math.sqrt(ps.length); - final int row = i / latticeParameter; + final int row = i / latticeParameter; final int column = i - row * latticeParameter; final int above = column + (row > 0 diff --git a/src/main/java/pulse/search/direction/pso/SwarmState.java b/src/main/java/pulse/search/direction/pso/SwarmState.java index 7baa955..df5b704 100644 --- a/src/main/java/pulse/search/direction/pso/SwarmState.java +++ b/src/main/java/pulse/search/direction/pso/SwarmState.java @@ -64,16 +64,16 @@ public void bestSoFar() { } } - + //determine the current best - ParticleState curBest = particles[bestIndex].getCurrentState(); - + ParticleState curBest = particles[bestIndex].getCurrentState(); + //is curBest the best so far? - if(bestSoFar == null || curBest.isBetterThan(bestSoFar) ) { + if (bestSoFar == null || curBest.isBetterThan(bestSoFar)) { this.bestSoFar = curBest; this.bestSoFarIndex = bestIndex; } - + } public NeighbourhoodTopology getNeighborhoodTopology() { @@ -113,4 +113,4 @@ public void setBestSoFarIndex(int bestSoFarIndex) { this.bestSoFarIndex = bestSoFarIndex; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/search/linear/GoldenSectionOptimiser.java b/src/main/java/pulse/search/linear/GoldenSectionOptimiser.java index 4c9f1be..85eab13 100644 --- a/src/main/java/pulse/search/linear/GoldenSectionOptimiser.java +++ b/src/main/java/pulse/search/linear/GoldenSectionOptimiser.java @@ -18,6 +18,11 @@ */ public class GoldenSectionOptimiser extends LinearOptimiser { + /** + * + */ + private static final long serialVersionUID = -369106060533186038L; + /** * The golden section φ, which is approximately equal to 0.618033989. */ diff --git a/src/main/java/pulse/search/linear/WolfeOptimiser.java b/src/main/java/pulse/search/linear/WolfeOptimiser.java index af3a2cd..d540ba3 100644 --- a/src/main/java/pulse/search/linear/WolfeOptimiser.java +++ b/src/main/java/pulse/search/linear/WolfeOptimiser.java @@ -25,7 +25,12 @@ * page */ public class WolfeOptimiser extends LinearOptimiser { - + + /** + * + */ + private static final long serialVersionUID = 5200832276052099700L; + private static WolfeOptimiser instance = new WolfeOptimiser(); /** @@ -38,7 +43,7 @@ public class WolfeOptimiser extends LinearOptimiser { * gradient projection, equal to {@value C2}. */ public final static double C2 = 0.8; - + private WolfeOptimiser() { super(); } @@ -66,34 +71,34 @@ private WolfeOptimiser() { */ @Override public double linearStep(GeneralTask task) throws SolverException { - + GradientGuidedPath p = (GradientGuidedPath) task.getIterativeState(); - + final Vector direction = p.getDirection(); final Vector g1 = p.getGradient(); - + final double G1P = g1.dot(direction); final double G1P_ABS = abs(G1P); - + var params = task.searchVector(); var vParams = params.toVector(); Segment segment = domain(params, direction); - + double cost1 = task.objectiveFunction(); - + double randomConfinedValue = 0; double g2p; - + var optimiser = (GradientBasedOptimiser) PathOptimiser.getInstance(); - + for (double initialLength = segment.length(); segment.length() / initialLength > searchResolution;) { - + randomConfinedValue = segment.randomValue(); - + final var newParams = vParams.sum(direction.multiply(randomConfinedValue)); - + task.assign(new ParameterVector(params, newParams)); - + final double cost2 = task.objectiveFunction(); /** @@ -105,7 +110,7 @@ public double linearStep(GeneralTask task) throws SolverException { segment.setMaximum(randomConfinedValue); continue; } - + final var g2 = optimiser.gradient(task); g2p = g2.dot(direction); @@ -121,16 +126,16 @@ public double linearStep(GeneralTask task) throws SolverException { * if( g2p >= C2*G1P ) break; */ segment.setMinimum(randomConfinedValue); - + } - + task.assign(params); p.setGradient(g1); - + return randomConfinedValue; - + } - + @Override public String toString() { return Messages.getString("WolfeSolver.Descriptor"); //$NON-NLS-1$ @@ -145,5 +150,5 @@ public String toString() { public static WolfeOptimiser getInstance() { return instance; } - + } diff --git a/src/main/java/pulse/search/statistics/AICStatistic.java b/src/main/java/pulse/search/statistics/AICStatistic.java index 529b2e6..5e04b38 100644 --- a/src/main/java/pulse/search/statistics/AICStatistic.java +++ b/src/main/java/pulse/search/statistics/AICStatistic.java @@ -7,11 +7,13 @@ */ public class AICStatistic extends ModelSelectionCriterion { + private static final long serialVersionUID = 8549601688520099629L; + public AICStatistic(OptimiserStatistic os) { super(os); } - public AICStatistic(AICStatistic another) { + public AICStatistic(ModelSelectionCriterion another) { super(another); } diff --git a/src/main/java/pulse/search/statistics/AbsoluteDeviations.java b/src/main/java/pulse/search/statistics/AbsoluteDeviations.java index 7113c14..37b2561 100644 --- a/src/main/java/pulse/search/statistics/AbsoluteDeviations.java +++ b/src/main/java/pulse/search/statistics/AbsoluteDeviations.java @@ -4,7 +4,6 @@ import static pulse.properties.NumericPropertyKeyword.OPTIMISER_STATISTIC; import pulse.search.GeneralTask; - /** * A statistical optimality criterion relying on absolute deviations or the L1 * norm condition. Similar to the least squares technique, it attempts to find a @@ -14,6 +13,8 @@ */ public class AbsoluteDeviations extends OptimiserStatistic { + private static final long serialVersionUID = 3385019714627583467L; + public AbsoluteDeviations() { super(); } @@ -25,6 +26,7 @@ public AbsoluteDeviations(AbsoluteDeviations another) { /** * Calculates the L1 norm statistic, which simply sums up the absolute * values of residuals. + * * @param t */ @Override diff --git a/src/main/java/pulse/search/statistics/AndersonDarlingTest.java b/src/main/java/pulse/search/statistics/AndersonDarlingTest.java index be69593..84d5104 100644 --- a/src/main/java/pulse/search/statistics/AndersonDarlingTest.java +++ b/src/main/java/pulse/search/statistics/AndersonDarlingTest.java @@ -16,13 +16,16 @@ */ public class AndersonDarlingTest extends NormalityTest { + private static final long serialVersionUID = -7471878404063688512L; + /** * This uses the SSJ statistical library to calculate the Anderson-Darling * test with the input parameters formed by the {@code task} residuals and a * normal distribution with zero mean and variance equal to the residuals * variance. + * * @param task - * @return + * @return */ @Override public boolean test(GeneralTask task) { @@ -33,7 +36,7 @@ public boolean test(GeneralTask task) { var testResult = GofStat.andersonDarling(residuals, nd); this.setStatistic(derive(TEST_STATISTIC, testResult[0])); - + //compare the p-value and the significance return testResult[1] > significance; } diff --git a/src/main/java/pulse/search/statistics/BICStatistic.java b/src/main/java/pulse/search/statistics/BICStatistic.java index 2c6edfe..c0d09aa 100644 --- a/src/main/java/pulse/search/statistics/BICStatistic.java +++ b/src/main/java/pulse/search/statistics/BICStatistic.java @@ -11,7 +11,9 @@ */ public class BICStatistic extends ModelSelectionCriterion { - public BICStatistic(BICStatistic another) { + private static final long serialVersionUID = 737642724262758403L; + + public BICStatistic(ModelSelectionCriterion another) { super(another); } diff --git a/src/main/java/pulse/search/statistics/CorrelationTest.java b/src/main/java/pulse/search/statistics/CorrelationTest.java index 670d7e5..ca7c942 100644 --- a/src/main/java/pulse/search/statistics/CorrelationTest.java +++ b/src/main/java/pulse/search/statistics/CorrelationTest.java @@ -22,15 +22,15 @@ public abstract class CorrelationTest extends PropertyHolder implements Reflexiv static { instanceDescriptor.setSelectedDescriptor(EmptyCorrelationTest.class.getSimpleName()); } - + public CorrelationTest() { //intentionally blank } public static CorrelationTest init() { - return instanceDescriptor.newInstance(CorrelationTest.class); + return instanceDescriptor.newInstance(CorrelationTest.class); } - + public final static InstanceDescriptor getTestDescriptor() { return instanceDescriptor; } diff --git a/src/main/java/pulse/search/statistics/EmptyCorrelationTest.java b/src/main/java/pulse/search/statistics/EmptyCorrelationTest.java index ba6195f..c69dbb3 100644 --- a/src/main/java/pulse/search/statistics/EmptyCorrelationTest.java +++ b/src/main/java/pulse/search/statistics/EmptyCorrelationTest.java @@ -2,6 +2,8 @@ public class EmptyCorrelationTest extends CorrelationTest { + private static final long serialVersionUID = -2462666081516562018L; + @Override public double evaluate(double[] x, double[] y) { return 0; diff --git a/src/main/java/pulse/search/statistics/EmptyTest.java b/src/main/java/pulse/search/statistics/EmptyTest.java index 573280c..4434f83 100644 --- a/src/main/java/pulse/search/statistics/EmptyTest.java +++ b/src/main/java/pulse/search/statistics/EmptyTest.java @@ -4,6 +4,8 @@ public class EmptyTest extends NormalityTest { + private static final long serialVersionUID = 5919796302195242667L; + /** * Always returns true */ diff --git a/src/main/java/pulse/search/statistics/FTest.java b/src/main/java/pulse/search/statistics/FTest.java index 9a39bc4..4d9da69 100644 --- a/src/main/java/pulse/search/statistics/FTest.java +++ b/src/main/java/pulse/search/statistics/FTest.java @@ -4,130 +4,137 @@ import pulse.tasks.Calculation; /** - * A static class for testing two calculations based on the Fischer test (F-Test) - * implemented in Apache Commons Math. + * A static class for testing two calculations based on the Fischer test + * (F-Test) implemented in Apache Commons Math. */ public class FTest { - + /** - * False-rejection probability for the F-test, equal to {@value FALSE_REJECTION_PROBABILITY} + * False-rejection probability for the F-test, equal to + * {@value FALSE_REJECTION_PROBABILITY} */ - public final static double FALSE_REJECTION_PROBABILITY = 0.05; - + private FTest() { //intentionall blank } - + /** * Tests two models to see which one is better according to the F-test + * * @param a a calculation * @param b another calculation - * @return {@code null} if the result is inconclusive, otherwise the - * best of two calculations. + * @return {@code null} if the result is inconclusive, otherwise the best of + * two calculations. * @see FTest.evaluate() */ - public static Calculation test(Calculation a, Calculation b) { - + double[] data = evaluate(a, b); - - Calculation best = null; - - if(data != null) { - + + Calculation best = null; + + if (data != null) { + //Under the null hypothesis the general model does not provide //a significantly better fit than the nested model - Calculation nested = findNested(a, b); - + //if the F-statistic is greater than the F-critical, reject the null hypothesis. - - if(nested == a) + if (nested == a) { best = data[0] > data[1] ? b : a; - else + } else { best = data[0] > data[1] ? a : b; - + } + } - + return best; - + } - + /** - * Evaluates the F-statistic for two calculations. + * Evaluates the F-statistic for two calculations. + * * @param a a calculation * @param b another calculation - * @return {@code null} if the test is inconclusive, i.e., if models are not - * nested, or if the model selection criteria are based on a statistic different - * from least-squares, or if the calculations refer to different data ranges. - * Otherwise returns an double array, consisting of two elements {@code [fStatistic, fCritical] } + * @return {@code null} if the test is inconclusive, i.e., if models are not + * nested, or if the model selection criteria are based on a statistic + * different from least-squares, or if the calculations refer to different + * data ranges. Otherwise returns an double array, consisting of two + * elements {@code [fStatistic, fCritical] } */ - public static double[] evaluate(Calculation a, Calculation b) { - + Calculation nested = findNested(a, b); - + double[] result = null; - + //if one of the models is nested into the other - if(nested != null) { + if (nested != null) { Calculation general = nested == a ? b : a; - + ResidualStatistic nestedResiduals = nested.getModelSelectionCriterion().getOptimiserStatistic(); - ResidualStatistic generalResiduals = general.getModelSelectionCriterion().getOptimiserStatistic(); + ResidualStatistic generalResiduals = general.getModelSelectionCriterion().getOptimiserStatistic(); final int nNested = nestedResiduals.getResiduals().size(); //sample size final int nGeneral = generalResiduals.getResiduals().size(); //sample size - + //if both models use a sum-of-square statistic for the model selection criteria //and if both calculations refer to the same calculation range - if(nestedResiduals.getClass() == generalResiduals.getClass() - && nestedResiduals.getClass() == SumOfSquares.class + if (nestedResiduals.getClass() == generalResiduals.getClass() + && nestedResiduals.getClass() == SumOfSquares.class && nNested == nGeneral) { - - double rssNested = ( (Number) ((SumOfSquares)nestedResiduals).getStatistic().getValue() ).doubleValue(); - double rssGeneral = ( (Number) ((SumOfSquares)generalResiduals).getStatistic().getValue() ).doubleValue(); - - int kGeneral = general.getModelSelectionCriterion().getNumVariables(); - int kNested = nested.getModelSelectionCriterion().getNumVariables(); - - double fStatistic = (rssNested - rssGeneral) - /(kGeneral - kNested) - /(rssGeneral/(nGeneral - kGeneral)); - - var fDistribution = new FDistribution(kGeneral - kNested, nGeneral - kGeneral); - - double fCritical = fDistribution.inverseCumulativeProbability(1.0 - FALSE_REJECTION_PROBABILITY); - - result = new double[]{fStatistic, fCritical}; - + + double rssNested = ((Number) ((SumOfSquares) nestedResiduals).getStatistic().getValue()).doubleValue(); + double rssGeneral = ((Number) ((SumOfSquares) generalResiduals).getStatistic().getValue()).doubleValue(); + + int kGeneral = general.getModelSelectionCriterion().getNumVariables(); + int kNested = nested.getModelSelectionCriterion().getNumVariables(); + + if (kGeneral > kNested && nGeneral > kGeneral) { + + double fStatistic = (rssNested - rssGeneral) + / (kGeneral - kNested) + / (rssGeneral / (nGeneral - kGeneral)); + + var fDistribution = new FDistribution(kGeneral - kNested, nGeneral - kGeneral); + + double fCritical = fDistribution.inverseCumulativeProbability(1.0 - FALSE_REJECTION_PROBABILITY); + + result = new double[]{fStatistic, fCritical}; + + } + } - + } - + return result; - + } - + /** - * Tests two models to see which one is nested in the other. A model is - * considered nested if it refers to the same class of problems but has + * Tests two models to see which one is nested in the other. A model is + * considered nested if it refers to the same class of problems but has * fewer parameters. + * * @param a a calculation * @param b another calculation * @return {@code null} if the models refer to different problem classes. * Otherwise returns the model that is nested in the second model. */ - public static Calculation findNested(Calculation a, Calculation b) { - if(a.getProblem().getClass() != b.getProblem().getClass()) - return null; - - int aParams = a.getModelSelectionCriterion().getNumVariables(); - int bParams = b.getModelSelectionCriterion().getNumVariables(); - - return aParams > bParams ? b : a; + var classA = a.getProblem().getClass(); + var classB = b.getProblem().getClass(); + if (!classA.isAssignableFrom(classB) && !classB.isAssignableFrom(classA)) { + return null; + } + + int aParams = a.getModelSelectionCriterion().getNumVariables(); + int bParams = b.getModelSelectionCriterion().getNumVariables(); + + return aParams > bParams ? b : a; } - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/search/statistics/KSTest.java b/src/main/java/pulse/search/statistics/KSTest.java index 7d5a854..70350d3 100644 --- a/src/main/java/pulse/search/statistics/KSTest.java +++ b/src/main/java/pulse/search/statistics/KSTest.java @@ -21,10 +21,10 @@ public class KSTest extends NormalityTest { @Override public boolean test(GeneralTask task) { evaluate(task); - - this.setStatistic(derive(TEST_STATISTIC, - TestUtils.kolmogorovSmirnovStatistic(nd, residuals))); - return !TestUtils.kolmogorovSmirnovTest(nd, residuals, this.significance); + + this.setStatistic(derive(TEST_STATISTIC, + TestUtils.kolmogorovSmirnovStatistic(nd, residuals))); + return !TestUtils.kolmogorovSmirnovTest(nd, residuals, this.significance); } @Override diff --git a/src/main/java/pulse/search/statistics/ModelSelectionCriterion.java b/src/main/java/pulse/search/statistics/ModelSelectionCriterion.java index fbba1cc..6951845 100644 --- a/src/main/java/pulse/search/statistics/ModelSelectionCriterion.java +++ b/src/main/java/pulse/search/statistics/ModelSelectionCriterion.java @@ -37,6 +37,35 @@ public ModelSelectionCriterion(ModelSelectionCriterion another) { this.criterion = another.criterion; } + @Override + public int hashCode() { + int hash = 7; + hash = 43 * hash + this.kq; + hash = 43 * hash + (int) (Double.doubleToLongBits(this.criterion) ^ (Double.doubleToLongBits(this.criterion) >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ModelSelectionCriterion other = (ModelSelectionCriterion) obj; + if (this.kq != other.kq) { + return false; + } + if (Double.doubleToLongBits(this.criterion) != Double.doubleToLongBits(other.criterion)) { + return false; + } + return true; + } + @Override public void evaluate(GeneralTask t) { kq = t.searchVector().dimension(); //number of parameters diff --git a/src/main/java/pulse/search/statistics/NormalityTest.java b/src/main/java/pulse/search/statistics/NormalityTest.java index f383a83..498a7f0 100644 --- a/src/main/java/pulse/search/statistics/NormalityTest.java +++ b/src/main/java/pulse/search/statistics/NormalityTest.java @@ -20,9 +20,10 @@ * residuals. As this is the pre-requisite for optimisers based on the ordinary * least-square statistic, the normality test can also be used to estimate if a * fit 'failed' or 'succeeded' in describing the data. - * + * * The test consists in testing the relation statistic < critValue, - * where the critical value is determined based on a given level of significance. + * where the critical value is determined based on a given level of + * significance. * */ public abstract class NormalityTest extends ResidualStatistic { diff --git a/src/main/java/pulse/search/statistics/PearsonCorrelation.java b/src/main/java/pulse/search/statistics/PearsonCorrelation.java index 401413c..50ffe90 100644 --- a/src/main/java/pulse/search/statistics/PearsonCorrelation.java +++ b/src/main/java/pulse/search/statistics/PearsonCorrelation.java @@ -9,6 +9,8 @@ */ public class PearsonCorrelation extends CorrelationTest { + private static final long serialVersionUID = 4819197257434836120L; + @Override public double evaluate(double[] x, double[] y) { return (new PearsonsCorrelation()).correlation(x, y); diff --git a/src/main/java/pulse/search/statistics/RSquaredTest.java b/src/main/java/pulse/search/statistics/RSquaredTest.java index 0feceed..807c39d 100644 --- a/src/main/java/pulse/search/statistics/RSquaredTest.java +++ b/src/main/java/pulse/search/statistics/RSquaredTest.java @@ -16,6 +16,7 @@ */ public class RSquaredTest extends NormalityTest { + private static final long serialVersionUID = -2022982190434832373L; private SumOfSquares sos; private static NumericProperty signifiance = derive(SIGNIFICANCE, 0.2); @@ -55,13 +56,13 @@ public void evaluate(GeneralTask t) { final double mean = mean(yr); double TSS = 0; int size = yr.size(); - + for (int i = 0; i < size; i++) { TSS += pow(yr.get(i) - mean, 2); } TSS /= size; - + setStatistic(derive(TEST_STATISTIC, (1. - (double) sos.getStatistic().getValue() / TSS))); } diff --git a/src/main/java/pulse/search/statistics/RangePenalisedLeastSquares.java b/src/main/java/pulse/search/statistics/RangePenalisedLeastSquares.java index af8710f..68943a7 100644 --- a/src/main/java/pulse/search/statistics/RangePenalisedLeastSquares.java +++ b/src/main/java/pulse/search/statistics/RangePenalisedLeastSquares.java @@ -11,8 +11,9 @@ */ public class RangePenalisedLeastSquares extends SumOfSquares { + private static final long serialVersionUID = 4068238957339821770L; private double lambda = 0.1; - + public RangePenalisedLeastSquares() { super(); } @@ -43,7 +44,7 @@ public void evaluate(GeneralTask t) { var x = t.getInput().getX(); double partialRange = t.getInput().bounds().length(); double fullRange = x.get(x.size() - 1) - x.get(IndexRange.closestLeft(0.0, x)); - final double statistic = ssr + lambda * (fullRange - partialRange)/fullRange; + final double statistic = ssr + lambda * (fullRange - partialRange) / fullRange; setStatistic(derive(OPTIMISER_STATISTIC, statistic)); } @@ -51,7 +52,7 @@ public void evaluate(GeneralTask t) { public String getDescriptor() { return "Range-Penalised Least Squares"; } - + @Override public OptimiserStatistic copy() { return new RangePenalisedLeastSquares(this); diff --git a/src/main/java/pulse/search/statistics/RegularisedLeastSquares.java b/src/main/java/pulse/search/statistics/RegularisedLeastSquares.java index 6fcd893..06bf1ff 100644 --- a/src/main/java/pulse/search/statistics/RegularisedLeastSquares.java +++ b/src/main/java/pulse/search/statistics/RegularisedLeastSquares.java @@ -13,8 +13,9 @@ */ public class RegularisedLeastSquares extends SumOfSquares { + private static final long serialVersionUID = -7398979361944447180L; private double lambda = 1e-4; - + public RegularisedLeastSquares() { super(); } diff --git a/src/main/java/pulse/search/statistics/ResidualStatistic.java b/src/main/java/pulse/search/statistics/ResidualStatistic.java index a00ad58..7185d5a 100644 --- a/src/main/java/pulse/search/statistics/ResidualStatistic.java +++ b/src/main/java/pulse/search/statistics/ResidualStatistic.java @@ -6,6 +6,7 @@ import static pulse.properties.NumericPropertyKeyword.OPTIMISER_STATISTIC; import java.util.List; +import java.util.Objects; import pulse.DiscreteInput; import pulse.Response; import pulse.input.IndexRange; @@ -31,6 +32,39 @@ public abstract class ResidualStatistic extends Statistic { private List rx; private List ry; + @Override + public int hashCode() { + int hash = 5; + hash = 53 * hash + (int) (Double.doubleToLongBits(this.statistic) ^ (Double.doubleToLongBits(this.statistic) >>> 32)); + hash = 53 * hash + Objects.hashCode(this.rx); + hash = 53 * hash + Objects.hashCode(this.ry); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ResidualStatistic other = (ResidualStatistic) obj; + if (Double.doubleToLongBits(this.statistic) != Double.doubleToLongBits(other.statistic)) { + return false; + } + if (!Objects.equals(this.rx, other.rx)) { + return false; + } + if (!Objects.equals(this.ry, other.ry)) { + return false; + } + return true; + } + public ResidualStatistic() { super(); ry = new ArrayList<>(); @@ -40,8 +74,8 @@ public ResidualStatistic() { public ResidualStatistic(ResidualStatistic another) { this.statistic = another.statistic; - ry = new ArrayList<>(); - rx = new ArrayList<>(); + ry = new ArrayList<>(another.rx); + rx = new ArrayList<>(another.ry); } /** @@ -68,9 +102,8 @@ public ResidualStatistic(ResidualStatistic another) { public final void calculateResiduals(DiscreteInput reference, Response estimate, int min, int max) { var y = reference.getY(); var x = reference.getX(); - + //if size has not changed, use the old list - if (ry.size() == max - min + 1) { for (int i = min; i < max; i++) { @@ -79,13 +112,10 @@ public final void calculateResiduals(DiscreteInput reference, Response estimate, } - } - - //else create a new list - + } //else create a new list else { - rx = x.subList(min, max); + rx = new ArrayList<>(x.subList(min, max)); ry.clear(); for (int i = min; i < max; i++) { @@ -97,18 +127,18 @@ public final void calculateResiduals(DiscreteInput reference, Response estimate, } } - + public void calculateResiduals(DiscreteInput reference, Response estimate) { var y = reference.getY(); var x = reference.getX(); - + var estimateRange = estimate.accessibleRange(); int min = (int) Math.max(reference.getIndexRange().getLowerBound(), - IndexRange.closestLeft(estimateRange.getMinimum(), x) ); + IndexRange.closestLeft(estimateRange.getMinimum(), x)); int max = (int) Math.min(reference.getIndexRange().getUpperBound(), - IndexRange.closestRight(estimateRange.getMaximum(), x) ); - + IndexRange.closestRight(estimateRange.getMaximum(), x)); + calculateResiduals(reference, estimate, min, max); } diff --git a/src/main/java/pulse/search/statistics/SpearmansCorrelationTest.java b/src/main/java/pulse/search/statistics/SpearmansCorrelationTest.java index 8bb4507..c073c64 100644 --- a/src/main/java/pulse/search/statistics/SpearmansCorrelationTest.java +++ b/src/main/java/pulse/search/statistics/SpearmansCorrelationTest.java @@ -9,6 +9,8 @@ */ public class SpearmansCorrelationTest extends CorrelationTest { + private static final long serialVersionUID = -8027167403407629716L; + @Override public double evaluate(double[] x, double[] y) { return (new SpearmansCorrelation()).correlation(x, y); diff --git a/src/main/java/pulse/search/statistics/Statistic.java b/src/main/java/pulse/search/statistics/Statistic.java index 46a7f21..30d056e 100644 --- a/src/main/java/pulse/search/statistics/Statistic.java +++ b/src/main/java/pulse/search/statistics/Statistic.java @@ -13,5 +13,5 @@ public abstract class Statistic extends PropertyHolder implements Reflexive { public abstract void evaluate(GeneralTask t); - -} \ No newline at end of file + +} diff --git a/src/main/java/pulse/search/statistics/SumOfSquares.java b/src/main/java/pulse/search/statistics/SumOfSquares.java index f264ed8..e72e41f 100644 --- a/src/main/java/pulse/search/statistics/SumOfSquares.java +++ b/src/main/java/pulse/search/statistics/SumOfSquares.java @@ -11,6 +11,8 @@ */ public class SumOfSquares extends OptimiserStatistic { + private static final long serialVersionUID = 3959714755977689591L; + public SumOfSquares() { super(); } @@ -38,7 +40,6 @@ public SumOfSquares(SumOfSquares sos) { * @param t The task containing the reference and calculated curves * @see calculateResiduals() */ - @Override public void evaluate(GeneralTask t) { calculateResiduals(t); diff --git a/src/main/java/pulse/tasks/Calculation.java b/src/main/java/pulse/tasks/Calculation.java index 4755a1d..db6b25d 100644 --- a/src/main/java/pulse/tasks/Calculation.java +++ b/src/main/java/pulse/tasks/Calculation.java @@ -9,6 +9,7 @@ import static pulse.util.Reflexive.instantiate; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import pulse.Response; @@ -27,15 +28,18 @@ import pulse.search.statistics.FTest; import pulse.search.statistics.ModelSelectionCriterion; import pulse.search.statistics.OptimiserStatistic; +import pulse.search.statistics.Statistic; import pulse.tasks.logs.Status; import pulse.tasks.processing.Result; import pulse.ui.components.PropertyHolderTable; import pulse.util.InstanceDescriptor; import pulse.util.PropertyEvent; import pulse.util.PropertyHolder; +import pulse.util.UpwardsNavigable; public class Calculation extends PropertyHolder implements Comparable, Response { + private static final long serialVersionUID = 8098141563821512602L; private Status status; public final static double RELATIVE_TIME_MARGIN = 1.01; @@ -57,7 +61,7 @@ public Calculation(SearchTask t) { status = INCOMPLETE; this.initOptimiser(); setParent(t); - instanceDescriptor.addListener(() -> initModelCriterion()); + instanceDescriptor.addListener(() -> initModelCriterion(rs)); } /** @@ -66,14 +70,23 @@ public Calculation(SearchTask t) { * @param c another calculation to be archived. */ public Calculation(Calculation c) { - this.problem = c.problem.copy(); - this.scheme = c.scheme.copy(); - this.rs = c.rs.copy(); - this.os = c.os.copy(); - this.status = c.status; + problem = c.problem.copy(); + scheme = c.scheme.copy(); + rs = c.rs.copy(); + os = c.os.copy(); + status = c.status; if (c.getResult() != null) { - this.result = new Result(c.getResult()); + result = new Result(c.getResult()); } + instanceDescriptor.addListener(() -> initModelCriterion(rs)); + } + + public void conformTo(UpwardsNavigable owner) { + problem.setParent(owner); + scheme.setParent(owner); + rs.setParent(owner); + os.setParent(owner); + result.setParent(owner); } public void clear() { @@ -99,7 +112,7 @@ public void clear() { public void setProblem(Problem problem, ExperimentalData curve) { this.problem = problem; problem.setParent(this); - problem.removeHeatingCurveListeners(); + problem.removeListeners(); addProblemListeners(problem, curve); } @@ -189,31 +202,40 @@ public Status getStatus() { */ public boolean setStatus(Status status) { - boolean changeStatus = true; - - switch (this.status) { - case QUEUED: - case IN_PROGRESS: - switch (status) { - case QUEUED: - case READY: - case INCOMPLETE: - changeStatus = false; - break; - default: - } - break; - case FAILED: - case EXECUTION_ERROR: - case INCOMPLETE: - //if the TaskManager attempts to run this calculation - changeStatus = status != Status.QUEUED; - break; - default: - } + boolean changeStatus = false; + + if (this.getStatus() != status) { + + changeStatus = true; + + //current status is given by ** this.status ** + //new status is the ** argument ** of this method + switch (this.status) { + case QUEUED: + //do not change status to queued, ready or incomplete if already in progress + case IN_PROGRESS: + switch (status) { + case QUEUED: + case READY: + case INCOMPLETE: + changeStatus = false; + break; + default: + } + break; + case FAILED: + case EXECUTION_ERROR: + case INCOMPLETE: + //if the TaskManager attempts to run this calculation + changeStatus = status != Status.QUEUED; + break; + default: + } + + if (changeStatus) { + this.status = status; + } - if (changeStatus) { - this.status = status; } return changeStatus; @@ -248,7 +270,7 @@ public ModelSelectionCriterion getModelSelectionCriterion() { public void setOptimiserStatistic(OptimiserStatistic os) { this.os = os; os.setParent(this); - initModelCriterion(); + initModelCriterion(os); } @Override @@ -263,11 +285,11 @@ public Problem getProblem() { public void initOptimiser() { this.setOptimiserStatistic( instantiate(OptimiserStatistic.class, OptimiserStatistic.getSelectedOptimiserDescriptor())); - this.initModelCriterion(); + initModelCriterion(os); } - public void initModelCriterion() { - setModelSelectionCriterion(instanceDescriptor.newInstance(ModelSelectionCriterion.class, os)); + protected void initModelCriterion(Statistic res) { + setModelSelectionCriterion(instanceDescriptor.newInstance(ModelSelectionCriterion.class, res)); } public DifferenceScheme getScheme() { @@ -319,27 +341,6 @@ public int compareTo(Calculation arg0) { return sThis.compareTo(sAnother); } - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - - if (o == null) { - return false; - } - - if (!(o instanceof Calculation)) { - return false; - } - - var c = (Calculation) o; - - return (os.getStatistic().equals(c.getOptimiserStatistic().getStatistic()) - && rs.getStatistic().equals(c.getModelSelectionCriterion().getStatistic())); - - } - public static InstanceDescriptor getModelSelectionDescriptor() { return instanceDescriptor; } @@ -359,14 +360,14 @@ public void setResult(Result result) { public double evaluate(double t) { return problem.getHeatingCurve().interpolateSignalAt(t); } - + @Override public Segment accessibleRange() { var hc = problem.getHeatingCurve(); return new Segment(hc.timeAt(0), hc.timeLimit()); } - /** + /** * This will use the current {@code DifferenceScheme} to solve the * {@code Problem} for this {@code SearchTask} and calculate the SSR value * showing how well (or bad) the calculated solution describes the @@ -376,7 +377,6 @@ public Segment accessibleRange() { * @return the value of SSR (sum of squared residuals). * @throws pulse.problem.schemes.solvers.SolverException */ - @Override public double objectiveFunction(GeneralTask task) throws SolverException { process(); @@ -384,4 +384,37 @@ public double objectiveFunction(GeneralTask task) throws SolverException { return (double) os.getStatistic().getValue(); } -} \ No newline at end of file + @Override + public int hashCode() { + int hash = 7; + hash = 79 * hash + Objects.hashCode(this.problem); + hash = 79 * hash + Objects.hashCode(this.scheme); + hash = 79 * hash + Objects.hashCode(this.result); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Calculation other = (Calculation) obj; + if (!Objects.equals(this.problem, other.problem)) { + return false; + } + if (!Objects.equals(this.scheme, other.scheme)) { + return false; + } + if (!Objects.equals(this.result, other.result)) { + return false; + } + return true; + } + +} diff --git a/src/main/java/pulse/tasks/Identifier.java b/src/main/java/pulse/tasks/Identifier.java index f640fbd..2a14adc 100644 --- a/src/main/java/pulse/tasks/Identifier.java +++ b/src/main/java/pulse/tasks/Identifier.java @@ -15,6 +15,7 @@ */ public class Identifier extends NumericProperty { + private static final long serialVersionUID = 3751417739136256453L; private static int lastId = -1; private Identifier(int value, boolean addToList) { @@ -57,5 +58,5 @@ public static Identifier externalIdentifier(int id) { public String toString() { return Messages.getString("Identifier.Tag") + " " + getValue(); } - -} + +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/SearchTask.java b/src/main/java/pulse/tasks/SearchTask.java index 733a939..ed814ae 100644 --- a/src/main/java/pulse/tasks/SearchTask.java +++ b/src/main/java/pulse/tasks/SearchTask.java @@ -29,6 +29,7 @@ import pulse.input.ExperimentalData; import pulse.input.InterpolationDataset; +import pulse.input.listeners.ExternalDatasetListener; import pulse.math.ParameterIdentifier; import pulse.math.ParameterVector; import pulse.problem.schemes.solvers.SolverException; @@ -62,23 +63,27 @@ */ public class SearchTask extends GeneralTask { + /** + * + */ + private static final long serialVersionUID = -6763815749875446528L; private Calculation current; private List stored; private ExperimentalData curve; private Log log; - private final CorrelationBuffer correlationBuffer; + private CorrelationBuffer correlationBuffer; private CorrelationTest correlationTest; private NormalityTest normalityTest; - private final Identifier identifier; + private Identifier identifier; /** * If {@code SearchTask} finishes, and its R2 value is * lower than this constant, the result will be considered * {@code AMBIGUOUS}. */ - private final List listeners; - private final List statusChangeListeners; + private transient List listeners; + private transient List statusChangeListeners; /** *

@@ -92,27 +97,29 @@ public class SearchTask extends GeneralTask { * @param curve the {@code ExperimentalData} */ public SearchTask(ExperimentalData curve) { - super(); - this.statusChangeListeners = new CopyOnWriteArrayList<>(); - this.listeners = new CopyOnWriteArrayList<>(); current = new Calculation(this); this.identifier = new Identifier(); this.curve = curve; curve.setParent(this); correlationBuffer = new CorrelationBuffer(); + initListeners(); clear(); - addListeners(); - } - - private void addListeners() { - InterpolationDataset.addListener(e -> { - if (current.getProblem() != null) { - var p = current.getProblem().getProperties(); - if (p.areThermalPropertiesLoaded()) { - p.useTheoreticalEstimates(curve); - } + } + + private void updateThermalProperties() { + if (current.getProblem() != null) { + var p = current.getProblem().getProperties(); + if (p.areThermalPropertiesLoaded()) { + p.useTheoreticalEstimates(curve); } - }); + } + } + + @Override + public void initListeners() { + super.initListeners(); + this.statusChangeListeners = new CopyOnWriteArrayList<>(); + this.listeners = new CopyOnWriteArrayList<>(); /** * Sets the difference scheme's time limit to the upper bound of the @@ -153,13 +160,13 @@ public void clear() { //this.path = null; current.clear(); - this.checkProblems(true); + this.checkProblems(); } - + public List alteredParameters() { - return activeParameters().stream().map(key -> - this.numericProperty(key)).collect(Collectors.toList()); - } + return activeParameters().stream().map(key + -> this.numericProperty(key)).collect(Collectors.toList()); + } public void addTaskListener(DataCollectionListener toAdd) { listeners.add(toAdd); @@ -181,7 +188,7 @@ public void removeStatusChangeListeners() { public String toString() { return getIdentifier().toString(); } - + /** * Adopts the {@code curve} by this {@code SearchTask}. * @@ -209,39 +216,37 @@ public void setExperimentalCurve(ExperimentalData curve) { * using the {@code status.getDetails()} method. *

* - * @param updateStatus */ - public void checkProblems(boolean updateStatus) { + public void checkProblems() { var status = getStatus(); - if (status == DONE) { - return; - } - - var pathSolver = getInstance(); - var s = INCOMPLETE; - - if (current.getProblem() == null) { - s.setDetails(MISSING_PROBLEM_STATEMENT); - } else if (!current.getProblem().isReady()) { - s.setDetails(INSUFFICIENT_DATA_IN_PROBLEM_STATEMENT); - } else if (current.getScheme() == null) { - s.setDetails(MISSING_DIFFERENCE_SCHEME); - } else if (curve == null) { - s.setDetails(MISSING_HEATING_CURVE); - } else if (pathSolver == null) { - s.setDetails(MISSING_OPTIMISER); - } else if (getBuffer() == null) { - s.setDetails(MISSING_BUFFER); - } else if (!getInstance().compatibleWith(current.getOptimiserStatistic())) { - s.setDetails(INCOMPATIBLE_OPTIMISER); - } else { - s = READY; - } + if (status != DONE) { + + var pathSolver = getInstance(); + var s = INCOMPLETE; + + if (current.getProblem() == null) { + s.setDetails(MISSING_PROBLEM_STATEMENT); + } else if (!current.getProblem().isReady()) { + s.setDetails(INSUFFICIENT_DATA_IN_PROBLEM_STATEMENT); + } else if (current.getScheme() == null) { + s.setDetails(MISSING_DIFFERENCE_SCHEME); + } else if (curve == null) { + s.setDetails(MISSING_HEATING_CURVE); + } else if (pathSolver == null) { + s.setDetails(MISSING_OPTIMISER); + } else if (getBuffer() == null) { + s.setDetails(MISSING_BUFFER); + } else if (!getInstance().compatibleWith(current.getOptimiserStatistic())) { + s.setDetails(INCOMPATIBLE_OPTIMISER); + } else { + s = READY; + } - if (updateStatus) { setStatus(s); + } + } public Identifier getIdentifier() { @@ -263,12 +268,12 @@ private void notifyStatusListeners(StateEntry e) { l.onStatusChange(e); } } - + @Override public void run() { correlationBuffer.clear(); current.setResult(null); - + /* check of status */ switch (getStatus()) { case READY: @@ -278,13 +283,12 @@ public void run() { default: return; } - + current.getProblem().parameterListChanged(); // get updated list of parameters - setDefaultOptimiser(); - + super.run(); } - + /** * If the current task is either {@code IN_PROGRESS}, {@code QUEUED}, or * {@code READY}, terminates it by setting its status to {@code TERMINATED}. @@ -322,19 +326,23 @@ public CorrelationBuffer getCorrelationBuffer() { public CorrelationTest getCorrelationTest() { return correlationTest; } - + public List getStoredCalculations() { return this.stored; - } + } public void storeCalculation() { var copy = new Calculation(current); stored.add(copy); - } + } public void switchTo(Calculation calc) { - current = calc; + current.setParent(null); + current.conformTo(null); + current = new Calculation(calc); current.setParent(this); + calc.conformTo(calc); + current.setStatus(Status.READY); var e = new TaskRepositoryEvent(TaskRepositoryEvent.State.TASK_MODEL_SWITCH, this.getIdentifier()); fireRepositoryEvent(e); } @@ -352,9 +360,12 @@ public Calculation findBestCalculation() { } public void switchToBestModel() { - this.switchTo(findBestCalculation()); - var e = new TaskRepositoryEvent(TaskRepositoryEvent.State.BEST_MODEL_SELECTED, this.getIdentifier()); - fireRepositoryEvent(e); + var best = findBestCalculation(); + if (current != best && best != null) { + this.switchTo(best); + var e = new TaskRepositoryEvent(TaskRepositoryEvent.State.BEST_MODEL_SELECTED, this.getIdentifier()); + fireRepositoryEvent(e); + } } private void fireRepositoryEvent(TaskRepositoryEvent e) { @@ -363,19 +374,19 @@ private void fireRepositoryEvent(TaskRepositoryEvent e) { l.onTaskListChanged(e); } } - + @Override public boolean isInProgress() { return getStatus() == IN_PROGRESS; } - + @Override public void intermediateProcessing() { correlationBuffer.inflate(this); notifyDataListeners(new DataLogEntry(this)); } - - @Override + + @Override public void onSolverException(SolverException e) { setStatus(Status.troubleshoot(e)); } @@ -391,8 +402,8 @@ public void onSolverException(SolverException e) { */ @Override public ParameterVector searchVector() { - var ids = activeParameters().stream().map(id -> - new ParameterIdentifier(id)).collect(Collectors.toList()); + var ids = activeParameters().stream().map(id + -> new ParameterIdentifier(id)).collect(Collectors.toList()); var optimisationVector = new ParameterVector(ids); current.getProblem().optimisationVector(optimisationVector); @@ -452,8 +463,7 @@ public void postProcessing() { } } - - + /** * Finds what properties are being altered in the search of this SearchTask. * @@ -464,16 +474,14 @@ public void postProcessing() { public List activeParameters() { var flags = ActiveFlags.getAllFlags(); //problem dependent - var allActiveParams = ActiveFlags.selectActiveAndListed - (flags, current.getProblem()); + var allActiveParams = ActiveFlags.selectActiveAndListed(flags, current.getProblem()); //problem independent (lower/upper bound) - var listed = ActiveFlags.selectActiveAndListed - (flags, curve.getRange() ); - allActiveParams.addAll(listed); + var listed = ActiveFlags.selectActiveAndListed(flags, curve.getRange()); + allActiveParams.addAll(listed); return allActiveParams; } - - /** + + /** * Will return {@code true} if status could be updated. * * @param status the status of the task @@ -482,20 +490,18 @@ public List activeParameters() { * be updated at this time. * @see Calculation.setStatus() */ - public boolean setStatus(Status status) { Objects.requireNonNull(status); Status oldStatus = getStatus(); - boolean changed = current.setStatus(status) - && (oldStatus != getStatus()); + boolean changed = current.setStatus(status) && oldStatus != status; if (changed) { notifyStatusListeners(new StateEntry(this, status)); } return changed; } - + public Status getStatus() { return current.getStatus(); } @@ -522,7 +528,7 @@ public boolean equals(Object o) { return curve.equals(((SearchTask) o).curve); } - + @Override public String describe() { @@ -550,4 +556,4 @@ public Calculation getResponse() { return current; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/tasks/TaskManager.java b/src/main/java/pulse/tasks/TaskManager.java index 358403d..f3394fb 100644 --- a/src/main/java/pulse/tasks/TaskManager.java +++ b/src/main/java/pulse/tasks/TaskManager.java @@ -3,7 +3,6 @@ import static java.time.LocalDateTime.now; import static java.time.format.DateTimeFormatter.ISO_WEEK_DATE; import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toList; import static pulse.io.readers.ReaderManager.curveReaders; import static pulse.io.readers.ReaderManager.read; import static pulse.tasks.listeners.TaskRepositoryEvent.State.SHUTDOWN; @@ -18,22 +17,31 @@ import static pulse.util.Group.contents; import java.io.File; +import java.io.IOException; +import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; -import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import pulse.input.ExperimentalData; +import pulse.input.InterpolationDataset; import pulse.input.listeners.DataEvent; import pulse.input.listeners.DataEventType; +import pulse.input.listeners.ExternalDatasetListener; +import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.CONDUCTIVITY; +import static pulse.properties.NumericPropertyKeyword.DENSITY; +import static pulse.properties.NumericPropertyKeyword.EMISSIVITY; +import static pulse.properties.NumericPropertyKeyword.SPECIFIC_HEAT; import pulse.properties.SampleName; import pulse.search.direction.PathOptimiser; +import pulse.tasks.listeners.SessionListener; import pulse.tasks.listeners.TaskRepositoryEvent; import pulse.tasks.listeners.TaskRepositoryListener; import pulse.tasks.listeners.TaskSelectionEvent; @@ -45,7 +53,6 @@ import pulse.util.Group; import pulse.util.HierarchyListener; import pulse.util.PropertyHolder; -import pulse.util.ResourceMonitor; import pulse.util.UpwardsNavigable; /** @@ -57,43 +64,76 @@ *

* */ -public class TaskManager extends UpwardsNavigable { - - private static final TaskManager instance = new TaskManager(); +public final class TaskManager extends UpwardsNavigable { + /** + * + */ + private static final long serialVersionUID = -4255751786167667650L; private List tasks; private SearchTask selectedTask; - private boolean singleStatement = true; + private HierarchyListener statementListener; - private final ForkJoinPool taskPool; - - private final List selectionListeners; - private final List taskRepositoryListeners; + private transient List selectionListeners; + private transient List taskRepositoryListeners; + private transient List externalListeners; - private final static String DEFAULT_NAME = "Measurement_" + now().format(ISO_WEEK_DATE); + private static TaskManager instance = new TaskManager(); + + private static List globalListeners = new ArrayList<>(); + + private InterpolationDataset cpDataset; + private InterpolationDataset rhoDataset; - private final HierarchyListener statementListener = e -> { + private TaskManager() { + tasks = new ArrayList<>(); + initListeners(); + } + + /** + * Creates a list of property keywords that can be derived with help of the + * loaded data. For example, if heat capacity and density data is available, + * the returned list will contain {@code CONDUCTIVITY}. + * + * @return + */ + public List derivableProperties() { + var list = new ArrayList(); + if (cpDataset != null) { + list.add(SPECIFIC_HEAT); + } + if (rhoDataset != null) { + list.add(DENSITY); + } + if (rhoDataset != null && cpDataset != null) { + list.add(CONDUCTIVITY); + list.add(EMISSIVITY); + } + return list; + } + + @Override + public void initListeners() { + super.initListeners(); + selectionListeners = new CopyOnWriteArrayList<>(); + taskRepositoryListeners = new CopyOnWriteArrayList<>(); + externalListeners = new CopyOnWriteArrayList<>(); + statementListener = e -> { - if (!(e.getSource() instanceof PropertyHolder)) { + if (!(e.getSource() instanceof PropertyHolder)) { - var task = (SearchTask) e.getPropertyHolder().specificAncestor(SearchTask.class); - for (SearchTask t : tasks) { - if (t == task) { - continue; + var task = (SearchTask) e.getPropertyHolder().specificAncestor(SearchTask.class); + for (SearchTask t : tasks) { + if (t == task) { + continue; + } + t.update(e.getProperty()); } - t.update(e.getProperty()); - } - } - - }; + } - private TaskManager() { - tasks = new ArrayList<>(); - taskPool = new ForkJoinPool(ResourceMonitor.getInstance().getThreadsAvailable()); - selectionListeners = new CopyOnWriteArrayList<>(); - taskRepositoryListeners = new CopyOnWriteArrayList<>(); + }; addHierarchyListener(statementListener); } @@ -115,40 +155,41 @@ public static TaskManager getManagerInstance() { * @param t a {@code SearchTask} that will be executed */ public void execute(SearchTask t) { - t.checkProblems(t.getStatus() != Status.DONE); + t.checkProblems(); - //try to start cmputation + // try to start computation // notify listeners computation is about to start - if (!t.setStatus(QUEUED)) { + if (t.getStatus() != QUEUED && !t.setStatus(QUEUED)) { return; } - + // notify listeners calculation started notifyListeners(new TaskRepositoryEvent(TASK_SUBMITTED, t.getIdentifier())); - + // run task t -- after task completed, write result and trigger listeners CompletableFuture.runAsync(t).thenRun(() -> { - Calculation current = (Calculation)t.getResponse(); + Calculation current = (Calculation) t.getResponse(); var e = new TaskRepositoryEvent(TASK_FINISHED, t.getIdentifier()); if (null == current.getStatus()) { notifyListeners(e); - } - else switch (current.getStatus()) { - case DONE: - current.setResult(new Result(t, ResultFormat.getInstance())); - //notify listeners before the task is re-assigned - notifyListeners(e); - t.storeCalculation(); - break; - case AWAITING_TERMINATION: - t.setStatus(Status.TERMINATED); - break; - default: - notifyListeners(e); - break; + } else { + switch (current.getStatus()) { + case DONE: + current.setResult(new Result(t, ResultFormat.getInstance())); + //notify listeners before the task is re-assigned + notifyListeners(e); + t.storeCalculation(); + break; + case AWAITING_TERMINATION: + t.setStatus(Status.TERMINATED); + break; + default: + notifyListeners(e); + break; + } } }); - + } /** @@ -170,7 +211,7 @@ public void notifyListeners(TaskRepositoryEvent e) { */ public void executeAll() { - var queue = tasks.stream().filter(t -> { + tasks.stream().filter(t -> { switch (t.getStatus()) { case IN_PROGRESS: case EXECUTION_ERROR: @@ -178,11 +219,9 @@ public void executeAll() { default: return true; } - }).collect(toList()); - - for (SearchTask t : queue) { - taskPool.submit(() -> execute(t)); - } + }).forEach(t -> { + execute(t); + }); } @@ -216,7 +255,7 @@ public void cancelAllTasks() { } - private void fireTaskSelected(Object source) { + public void fireTaskSelected(Object source) { var e = new TaskSelectionEvent(source); for (var l : selectionListeners) { l.onSelectionChanged(e); @@ -259,7 +298,7 @@ public SampleName getSampleName() { return null; } - return ( (ExperimentalData) optional.get().getInput() ) + return ((ExperimentalData) optional.get().getInput()) .getMetadata().getSampleName(); } @@ -306,7 +345,7 @@ public SearchTask getTask(Identifier id) { */ public SearchTask getTask(int externalId) { var o = tasks.stream().filter(t - -> Integer.compare( ( (ExperimentalData) t.getInput()) + -> Integer.compare(((ExperimentalData) t.getInput()) .getMetadata().getExternalID(), externalId) == 0).findFirst(); return o.isPresent() ? o.get() : null; @@ -354,7 +393,7 @@ public void generateTask(File file) { */ public void generateTasks(List files) { requireNonNull(files, "Null list of files passed to generatesTasks(...)"); - + //this is the loader runnable submitted to the executor service Runnable loader = () -> { var pool = Executors.newSingleThreadExecutor(); @@ -468,8 +507,8 @@ public final void addTaskRepositoryListener(TaskRepositoryListener listener) { taskRepositoryListeners.add(listener); } - public TaskSelectionListener[] getSelectionListeners() { - return (TaskSelectionListener[]) selectionListeners.toArray(); + public List getSelectionListeners() { + return selectionListeners; } public void removeSelectionListeners() { @@ -505,13 +544,15 @@ public List getTaskRepositoryListeners() { @Override public String describe() { var name = getSampleName(); - return name == null || name.getValue() == null ? DEFAULT_NAME : name.toString(); + return name == null || name.getValue() == null + ? "Measurement_" + now().format(ISO_WEEK_DATE) + : name.toString(); } public void evaluate() { tasks.stream().forEach(t -> { - var properties = ( (Calculation) t.getResponse() ).getProblem().getProperties(); - var c = (ExperimentalData)t.getInput(); + var properties = ((Calculation) t.getResponse()).getProblem().getProperties(); + var c = (ExperimentalData) t.getInput(); properties.useTheoreticalEstimates(c); }); } @@ -535,6 +576,24 @@ public Set allGrouppedContents() { public boolean isSingleStatement() { return singleStatement; } + + public static void assumeNewState(TaskManager loaded) { + TaskManager.instance = null; + TaskManager.instance = loaded; + globalListeners.stream().forEach(l -> l.onNewSessionLoaded()); + } + + public void addExternalDatasetListener(ExternalDatasetListener edl) { + this.externalListeners.add(edl); + } + + public static void addSessionListener(SessionListener sl) { + globalListeners.add(sl); + } + + public static void removeSessionListeners() { + globalListeners.clear(); + } /** * Sets the flag to isolate or inter-connects changes in all instances of @@ -552,5 +611,35 @@ public void setSingleStatement(boolean singleStatement) { this.addHierarchyListener(statementListener); } } - -} + + /* + Serialization + */ + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + // default deserialization + ois.defaultReadObject(); + } + + public InterpolationDataset getDensityDataset() { + return rhoDataset; + } + + public InterpolationDataset getSpecificHeatDataset() { + return cpDataset; + } + + public void setDensityDataset(InterpolationDataset dataset) { + this.rhoDataset = dataset; + this.externalListeners.stream().forEach(l -> l.onDensityDataLoaded()); + evaluate(); + } + + public void setSpecificHeatDataset(InterpolationDataset dataset) { + this.cpDataset = dataset; + this.externalListeners.stream().forEach(l -> l.onSpecificHeatDataLoaded()); + evaluate(); + } + +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/listeners/DataCollectionListener.java b/src/main/java/pulse/tasks/listeners/DataCollectionListener.java index 5bbdafa..b1f2e4a 100644 --- a/src/main/java/pulse/tasks/listeners/DataCollectionListener.java +++ b/src/main/java/pulse/tasks/listeners/DataCollectionListener.java @@ -1,8 +1,10 @@ package pulse.tasks.listeners; +import java.io.Serializable; import pulse.tasks.logs.LogEntry; -public interface DataCollectionListener { +public interface DataCollectionListener extends Serializable { public void onDataCollected(LogEntry e); + } diff --git a/src/main/java/pulse/tasks/listeners/LogEntryListener.java b/src/main/java/pulse/tasks/listeners/LogEntryListener.java index 660ce18..e2d0392 100644 --- a/src/main/java/pulse/tasks/listeners/LogEntryListener.java +++ b/src/main/java/pulse/tasks/listeners/LogEntryListener.java @@ -1,9 +1,10 @@ package pulse.tasks.listeners; +import java.io.Serializable; import pulse.tasks.logs.Log; import pulse.tasks.logs.LogEntry; -public interface LogEntryListener { +public interface LogEntryListener extends Serializable { public void onNewEntry(LogEntry e); diff --git a/src/main/java/pulse/tasks/listeners/ResultFormatEvent.java b/src/main/java/pulse/tasks/listeners/ResultFormatEvent.java index 5407757..edd94e9 100644 --- a/src/main/java/pulse/tasks/listeners/ResultFormatEvent.java +++ b/src/main/java/pulse/tasks/listeners/ResultFormatEvent.java @@ -1,8 +1,9 @@ package pulse.tasks.listeners; +import java.io.Serializable; import pulse.tasks.processing.ResultFormat; -public class ResultFormatEvent { +public class ResultFormatEvent implements Serializable { private ResultFormat rf; diff --git a/src/main/java/pulse/tasks/listeners/ResultFormatListener.java b/src/main/java/pulse/tasks/listeners/ResultFormatListener.java index bfa0c1c..732563c 100644 --- a/src/main/java/pulse/tasks/listeners/ResultFormatListener.java +++ b/src/main/java/pulse/tasks/listeners/ResultFormatListener.java @@ -1,6 +1,8 @@ package pulse.tasks.listeners; -public interface ResultFormatListener { +import java.io.Serializable; + +public interface ResultFormatListener extends Serializable { public void resultFormatChanged(ResultFormatEvent rfe); diff --git a/src/main/java/pulse/tasks/listeners/SessionListener.java b/src/main/java/pulse/tasks/listeners/SessionListener.java new file mode 100644 index 0000000..30d1300 --- /dev/null +++ b/src/main/java/pulse/tasks/listeners/SessionListener.java @@ -0,0 +1,7 @@ +package pulse.tasks.listeners; + +public interface SessionListener { + + public void onNewSessionLoaded(); + +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/listeners/StatusChangeListener.java b/src/main/java/pulse/tasks/listeners/StatusChangeListener.java index 61b767c..3d9ba94 100644 --- a/src/main/java/pulse/tasks/listeners/StatusChangeListener.java +++ b/src/main/java/pulse/tasks/listeners/StatusChangeListener.java @@ -1,8 +1,9 @@ package pulse.tasks.listeners; +import java.io.Serializable; import pulse.tasks.logs.StateEntry; -public interface StatusChangeListener { +public interface StatusChangeListener extends Serializable { public void onStatusChange(StateEntry e); } diff --git a/src/main/java/pulse/tasks/listeners/TaskRepositoryEvent.java b/src/main/java/pulse/tasks/listeners/TaskRepositoryEvent.java index 0c232ab..f234b6a 100644 --- a/src/main/java/pulse/tasks/listeners/TaskRepositoryEvent.java +++ b/src/main/java/pulse/tasks/listeners/TaskRepositoryEvent.java @@ -21,7 +21,6 @@ public Identifier getId() { } public enum State { - /** * Indicates a task has been added to the repository. */ @@ -59,6 +58,10 @@ public enum State { * Indicates the task has discarded superfluous calculations. */ BEST_MODEL_SELECTED, + /** + * A new state has been loaded. + */ + NEW_STATE, /** * The repository has been shut down/ */ @@ -66,4 +69,4 @@ public enum State { } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/listeners/TaskRepositoryListener.java b/src/main/java/pulse/tasks/listeners/TaskRepositoryListener.java index d30cf03..639d989 100644 --- a/src/main/java/pulse/tasks/listeners/TaskRepositoryListener.java +++ b/src/main/java/pulse/tasks/listeners/TaskRepositoryListener.java @@ -1,6 +1,9 @@ package pulse.tasks.listeners; -public interface TaskRepositoryListener { +import java.io.Serializable; + +public interface TaskRepositoryListener extends Serializable { public void onTaskListChanged(TaskRepositoryEvent e); -} + +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/listeners/TaskSelectionEvent.java b/src/main/java/pulse/tasks/listeners/TaskSelectionEvent.java index 89ed065..c2ce45f 100644 --- a/src/main/java/pulse/tasks/listeners/TaskSelectionEvent.java +++ b/src/main/java/pulse/tasks/listeners/TaskSelectionEvent.java @@ -4,11 +4,6 @@ public class TaskSelectionEvent extends EventObject { - /** - * - */ - private static final long serialVersionUID = 4278832926994139917L; - public TaskSelectionEvent(Object source) { super(source); // TODO Auto-generated constructor stub diff --git a/src/main/java/pulse/tasks/listeners/TaskSelectionListener.java b/src/main/java/pulse/tasks/listeners/TaskSelectionListener.java index fb422b4..a4c3f1a 100644 --- a/src/main/java/pulse/tasks/listeners/TaskSelectionListener.java +++ b/src/main/java/pulse/tasks/listeners/TaskSelectionListener.java @@ -1,7 +1,9 @@ package pulse.tasks.listeners; -public interface TaskSelectionListener { +import java.io.Serializable; + +public interface TaskSelectionListener extends Serializable { public void onSelectionChanged(TaskSelectionEvent e); -} +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/logs/AbstractLogger.java b/src/main/java/pulse/tasks/logs/AbstractLogger.java new file mode 100644 index 0000000..966351a --- /dev/null +++ b/src/main/java/pulse/tasks/logs/AbstractLogger.java @@ -0,0 +1,83 @@ +package pulse.tasks.logs; + +import java.io.Serializable; +import java.util.concurrent.ExecutorService; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import javax.swing.JComponent; +import pulse.tasks.TaskManager; +import static pulse.tasks.logs.Status.DONE; +import pulse.util.Descriptive; + +public abstract class AbstractLogger implements Descriptive, Serializable { + + private ExecutorService updateExecutor; + + public AbstractLogger() { + updateExecutor = newSingleThreadExecutor(); + } + + public synchronized void update() { + var task = TaskManager.getManagerInstance().getSelectedTask(); + + if (task == null) { + return; + } + + var log = task.getLog(); + + if (log.isStarted()) { + post(log.lastEntry()); + } + + } + + public ExecutorService getUpdateExecutor() { + return updateExecutor; + } + + public synchronized void callUpdate() { + updateExecutor.submit(() -> update()); + } + + public void postAll() { + clear(); + + var task = TaskManager.getManagerInstance().getSelectedTask(); + + if (task != null) { + + var log = task.getLog(); + + if (log.isStarted()) { + + log.getLogEntries().stream().forEach(entry -> post(entry)); + + if (task.getStatus() == DONE) { + printTimeTaken(log); + } + + } + + } + + } + + @Override + public String describe() { + var task = TaskManager.getManagerInstance().getSelectedTask(); + return "Log" + (task == null ? "" : "_" + task.getIdentifier().getValue()); + } + + public abstract JComponent getGUIComponent(); + + public abstract void printTimeTaken(Log log); + + public abstract void post(LogEntry logEntry); + + public abstract void post(String text); + + public abstract void clear(); + + public abstract boolean isEmpty(); + +} diff --git a/src/main/java/pulse/tasks/logs/CorrelationLogEntry.java b/src/main/java/pulse/tasks/logs/CorrelationLogEntry.java index f6785bd..f87f078 100644 --- a/src/main/java/pulse/tasks/logs/CorrelationLogEntry.java +++ b/src/main/java/pulse/tasks/logs/CorrelationLogEntry.java @@ -35,12 +35,14 @@ public String toString() { for (ImmutablePair key : map.keySet()) { sb.append(""); sb.append(def(key.getFirst().getKeyword()).getAbbreviation(false)); - if(key.getFirst().getIndex() > 0) + if (key.getFirst().getIndex() > 0) { sb.append(" - ").append(key.getFirst().getIndex()); + } sb.append(""); sb.append(def(key.getSecond().getKeyword()).getAbbreviation(false)); - if(key.getSecond().getIndex() > 0) + if (key.getSecond().getIndex() > 0) { sb.append(" - ").append(key.getSecond().getIndex()); + } sb.append(""); if (test.compareToThreshold(map.get(key))) { sb.append(""); diff --git a/src/main/java/pulse/tasks/logs/DataLogEntry.java b/src/main/java/pulse/tasks/logs/DataLogEntry.java index 2fad022..4553272 100644 --- a/src/main/java/pulse/tasks/logs/DataLogEntry.java +++ b/src/main/java/pulse/tasks/logs/DataLogEntry.java @@ -5,6 +5,8 @@ import pulse.math.Parameter; import pulse.math.ParameterIdentifier; import pulse.properties.NumericProperties; +import static pulse.properties.NumericProperties.def; +import static pulse.properties.NumericPropertyKeyword.OBJECTIVE_FUNCTION; import pulse.tasks.SearchTask; import pulse.tasks.TaskManager; @@ -20,6 +22,7 @@ */ public class DataLogEntry extends LogEntry { + private static final long serialVersionUID = -8995240410369870205L; private List entry; /** @@ -54,10 +57,19 @@ public DataLogEntry(SearchTask task) { private void fill() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { var task = TaskManager.getManagerInstance().getTask(getIdentifier()); entry = task.searchVector().getParameters(); + //iteration var pval = task.getIterativeState().getIteration(); var pid = new Parameter(new ParameterIdentifier(pval.getType())); - pid.setValue( (int) pval.getValue() ); + pid.setValue((int) pval.getValue()); + //cost + var costId = new Parameter(new ParameterIdentifier(OBJECTIVE_FUNCTION)); + var costval = task.getIterativeState().getCost(); + // entry.add(0, pid); + if (NumericProperties.isValueSensible(def(OBJECTIVE_FUNCTION), costval)) { + costId.setValue(costval); + entry.add(costId); + } } public List getData() { @@ -90,15 +102,15 @@ public String toString() { var def = NumericProperties.def(p.getIdentifier().getKeyword()); boolean b = def.getValue() instanceof Integer; Number val; - if(b) { + if (b) { val = (int) Math.rint(p.getApparentValue()); - } else{ + } else { val = p.getApparentValue(); } def.setValue(val); sb.append(def.getAbbreviation(false)); int index = p.getIdentifier().getIndex(); - if(index > 0) { + if (index > 0) { sb.append(" - ").append(index); } sb.append("<"); diff --git a/src/main/java/pulse/tasks/logs/Details.java b/src/main/java/pulse/tasks/logs/Details.java index acaf6df..af2854e 100644 --- a/src/main/java/pulse/tasks/logs/Details.java +++ b/src/main/java/pulse/tasks/logs/Details.java @@ -42,22 +42,17 @@ public enum Details { PARAMETER_VALUES_NOT_SENSIBLE, MAX_ITERATIONS_REACHED, ABNORMAL_DISTRIBUTION_OF_RESIDUALS, - /** - * Indicates that the result table had not been updated, as the selected - * model produced results worse than expected by the model selection criterion. + * Indicates that the result table had not been updated, as the selected + * model produced results worse than expected by the model selection + * criterion. */ - CALCULATION_RESULTS_WORSE_THAN_PREVIOUSLY_OBTAINED, - - /** - * Indicates that the result table had been updated, as the current - * model selection criterion showed better result than already present. + * Indicates that the result table had been updated, as the current model + * selection criterion showed better result than already present. */ - BETTER_CALCULATION_RESULTS_THAN_PREVIOUSLY_OBTAINED, - SOLVER_ERROR; @Override diff --git a/src/main/java/pulse/tasks/logs/Log.java b/src/main/java/pulse/tasks/logs/Log.java index 5b51953..acdcde6 100644 --- a/src/main/java/pulse/tasks/logs/Log.java +++ b/src/main/java/pulse/tasks/logs/Log.java @@ -1,6 +1,8 @@ package pulse.tasks.logs; import java.time.LocalTime; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.SECONDS; import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; @@ -9,6 +11,8 @@ import pulse.tasks.SearchTask; import pulse.tasks.TaskManager; import pulse.tasks.listeners.LogEntryListener; +import pulse.tasks.listeners.TaskRepositoryEvent; +import pulse.tasks.listeners.TaskRepositoryEvent.State; import pulse.ui.Messages; import pulse.util.Group; @@ -19,12 +23,15 @@ */ public class Log extends Group { + private static final long serialVersionUID = 420096365502122145L; private List logEntries; private LocalTime start; private LocalTime end; - private final Identifier id; - private final List listeners; - private static boolean verbose = false; + private Identifier id; + private boolean finished; + private transient List listeners; + + private static boolean graphical = true; /** * Creates a {@code Log} for this {@code task} that will automatically store @@ -41,14 +48,43 @@ public Log(SearchTask task) { id = task.getIdentifier(); this.logEntries = new CopyOnWriteArrayList<>(); + initListeners(); + } + + @Override + public void initListeners() { + super.initListeners(); listeners = new CopyOnWriteArrayList<>(); + var instance = TaskManager.getManagerInstance(); + var existingTask = instance.getTask(id); + + if (existingTask != null) { + //task already exists - add listeners nwo + doAddListeners(existingTask); + } else { + //wait until task is added into repository + instance.addTaskRepositoryListener(event -> { + + if (event.getState() == State.TASK_ADDED && event.getId().equals(id)) { + + var task = TaskManager.getManagerInstance().getTask(id); + doAddListeners(task); + } + } + ); + + } + + } + + private void doAddListeners(SearchTask task) { task.addTaskListener(le -> { /** * Do these actions each time data has been collected for this task. */ - if (task.getStatus() != Status.INCOMPLETE && verbose) { + if (task.getStatus() != Status.INCOMPLETE) { logEntries.add(le); notifyListeners(le); } @@ -57,31 +93,32 @@ public Log(SearchTask task) { task.addStatusChangeListener((StateEntry e) -> { logEntries.add(e); - + if (e.getStatus() == Status.IN_PROGRESS) { start = e.getTime(); end = null; } else { end = e.getTime(); } - + notifyListeners(e); - + if (e.getState() == Status.DONE) { logFinished(); } } /** * Do these actions every time the task status has changed. - */ + */ ); - } private void logFinished() { + finished = true; listeners.stream().forEach(l -> l.onLogFinished(this)); } private void notifyListeners(LogEntry logEntry) { + finished = false; listeners.stream().forEach(l -> l.onNewEntry(logEntry)); } @@ -105,7 +142,11 @@ public final Identifier getIdentifier() { * @return {@code true} if the start time is not {@code null} */ public boolean isStarted() { - return start != null; + return logEntries.size() > 0; + } + + public boolean isFinished() { + return finished; } /** @@ -173,18 +214,31 @@ public LogEntry lastEntry() { * * @return {@code true} if the verbose flag is on */ - public static boolean isVerbose() { - return verbose; + public static boolean isGraphicalLog() { + return graphical; } /** * Sets the verbose flag to {@code verbose} * * @param verbose the new value of the flag - * @see isVerbose() + * @see #isGraphicalLog() + */ + public static void setGraphicalLog(boolean verbose) { + Log.graphical = verbose; + } + + /** + * Time taken where the first array element contains seconds [0] and the + * second contains milliseconds [1]. + * + * @return an array of long values that sum um to the time taken to process + * a task */ - public static void setVerbose(boolean verbose) { - Log.verbose = verbose; + public long[] timeTaken() { + var seconds = SECONDS.between(getStart(), getEnd()); + var ms = MILLIS.between(getStart(), getEnd()) - 1000L * seconds; + return new long[]{seconds, ms}; } } diff --git a/src/main/java/pulse/tasks/logs/LogEntry.java b/src/main/java/pulse/tasks/logs/LogEntry.java index 7841a91..6a6d644 100644 --- a/src/main/java/pulse/tasks/logs/LogEntry.java +++ b/src/main/java/pulse/tasks/logs/LogEntry.java @@ -1,5 +1,6 @@ package pulse.tasks.logs; +import java.io.Serializable; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Objects; @@ -17,10 +18,12 @@ *

* */ -public class LogEntry { +public class LogEntry implements Serializable { - private Identifier identifier; - private LocalTime time; + private static final long serialVersionUID = -6797821686964650045L; + private final Identifier identifier; + private final LocalTime time; + private final LogEntry previous; /** *

@@ -34,6 +37,16 @@ public LogEntry(SearchTask t) { Objects.requireNonNull(t, Messages.getString("LogEntry.NullTaskError")); time = LocalDateTime.now().toLocalTime(); identifier = t.getIdentifier(); + var list = t.getLog().getLogEntries(); + if (list != null && !list.isEmpty()) { + previous = list.get(list.size() - 1); + } else { + previous = null; + } + } + + public LogEntry getPreviousEntry() { + return previous; } public Identifier getIdentifier() { diff --git a/src/main/java/pulse/tasks/logs/StateEntry.java b/src/main/java/pulse/tasks/logs/StateEntry.java index 17333a6..e2ea9a5 100644 --- a/src/main/java/pulse/tasks/logs/StateEntry.java +++ b/src/main/java/pulse/tasks/logs/StateEntry.java @@ -8,6 +8,7 @@ public class StateEntry extends LogEntry { + private static final long serialVersionUID = 8380229394939453079L; private Status status; public StateEntry(SearchTask task, Status status) { @@ -41,7 +42,7 @@ public String toString() { if (status.getDetails() != NONE) { sb.append(" due to " + status.getDetails() + ""); } - if(status.getDetailedMessage().length() > 0) { + if (status.getDetailedMessage().length() > 0) { sb.append(" Details: "); sb.append(status.getDetailedMessage()); } diff --git a/src/main/java/pulse/tasks/logs/Status.java b/src/main/java/pulse/tasks/logs/Status.java index e2d7017..d479bc2 100644 --- a/src/main/java/pulse/tasks/logs/Status.java +++ b/src/main/java/pulse/tasks/logs/Status.java @@ -37,13 +37,10 @@ public enum Status { * Termination requested. */ AWAITING_TERMINATION(Color.DARK_GRAY), - /** * Task terminated */ - TERMINATED(Color.DARK_GRAY), - /** * Task has been queued and is waiting to be executed. */ @@ -82,11 +79,11 @@ public Details getDetails() { public void setDetails(Details details) { this.details = details; } - + public String getDetailedMessage() { return message; } - + public void setDetailedMessage(String str) { this.message = str; } @@ -133,20 +130,19 @@ public String getMessage() { } return sb.toString(); } - + public static Status troubleshoot(SolverException e1) { Objects.requireNonNull(e1, "Solver exception cannot be null when calling troubleshoot!"); Status status = null; - if(e1.getType() != SolverExceptionType.OPTIMISATION_TIMEOUT) { + if (e1.getType() != SolverExceptionType.OPTIMISATION_TIMEOUT) { status = Status.FAILED; status.setDetails(Details.SOLVER_ERROR); - status.setDetailedMessage(e1.getMessage()); - } - else { + status.setDetailedMessage(e1.getMessage()); + } else { status = Status.TIMEOUT; status.setDetails(Details.MAX_ITERATIONS_REACHED); } return status; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/tasks/processing/AverageResult.java b/src/main/java/pulse/tasks/processing/AverageResult.java index f5cdf85..f6186b0 100644 --- a/src/main/java/pulse/tasks/processing/AverageResult.java +++ b/src/main/java/pulse/tasks/processing/AverageResult.java @@ -18,6 +18,8 @@ */ public class AverageResult extends AbstractResult { + private static final long serialVersionUID = 5279249996318155238L; + private final List results; public final static int SIGNIFICANT_FIGURES = 2; @@ -28,11 +30,10 @@ public class AverageResult extends AbstractResult { *

* It will also use the {@code resultFormat}. A method will be invoked to: * (a) calculate the mean values of the list of {@code NumericProperty} - * according to the {@code resultFormat}; (b) calculate the statistical error; - * (c) create a {@code BigDecimal} representation - * of the values and the errors, so that only {@value SIGNIFICANT_FIGURES} - * significant figures are left for consistency between the {@code value} - * and the {@code error}. + * according to the {@code resultFormat}; (b) calculate the statistical + * error; (c) create a {@code BigDecimal} representation of the values and + * the errors, so that only {@value SIGNIFICANT_FIGURES} significant figures + * are left for consistency between the {@code value} and the {@code error}. *

* * @param res a list of {@code AbstractResult}s that are going to be @@ -72,16 +73,16 @@ private void calculate() { if (!Double.isFinite(err[j])) { p = derive(key, av[j]); // ignore error as the value is not finite - } else if(NumericProperties.def(key).getValue() instanceof Double) { - var stdBig = new BigDecimal(err[j]); - var avBig = new BigDecimal(av[j]); + } else if (NumericProperties.def(key).getValue() instanceof Double) { + var stdBig = new BigDecimal(err[j]); + var avBig = new BigDecimal(av[j]); var error = stdBig.setScale( SIGNIFICANT_FIGURES - stdBig.precision() + stdBig.scale(), RoundingMode.HALF_UP); - var mean = stdBig.precision() > 1 ? - avBig.setScale(error.scale(), RoundingMode.CEILING) + var mean = stdBig.precision() > 1 + ? avBig.setScale(error.scale(), RoundingMode.CEILING) : avBig; p = derive(key, mean.doubleValue()); @@ -89,8 +90,8 @@ private void calculate() { } else { //if integer - p = derive(key, (int)Math.round( av[j] ) ); - p.setError((int)Math.round( err[j] ) ); + p = derive(key, (int) Math.round(av[j])); + p.setError((int) Math.round(err[j])); } addProperty(p); diff --git a/src/main/java/pulse/tasks/processing/Buffer.java b/src/main/java/pulse/tasks/processing/Buffer.java index e3a1684..b257e59 100644 --- a/src/main/java/pulse/tasks/processing/Buffer.java +++ b/src/main/java/pulse/tasks/processing/Buffer.java @@ -8,10 +8,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; +import org.apache.commons.math3.stat.regression.SimpleRegression; +import pulse.math.ParameterIdentifier; import pulse.math.ParameterVector; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.OBJECTIVE_FUNCTION; import pulse.properties.Property; import pulse.search.GeneralTask; import pulse.util.PropertyHolder; @@ -25,6 +29,10 @@ */ public class Buffer extends PropertyHolder { + /** + * + */ + private static final long serialVersionUID = 3613745885879508057L; private ParameterVector[] data; private double[] statistic; private static int size = (int) def(BUFFER_SIZE).getValue(); @@ -48,7 +56,7 @@ public ParameterVector[] getData() { /* * Re-inits the storage. */ - public void init() { + public final void init() { this.data = new ParameterVector[size]; statistic = new double[size]; } @@ -105,11 +113,17 @@ public double average(NumericPropertyKeyword index) { double av = 0; - for (ParameterVector v : data) { - av += v.getParameterValue(index, 0); + if (index == OBJECTIVE_FUNCTION) { + av = Arrays.stream(statistic).average().getAsDouble(); + } else { + + for (ParameterVector v : data) { + av += v.getParameterValue(index, 0); + } + av /= data.length; } - return av / data.length; + return av; } diff --git a/src/main/java/pulse/tasks/processing/CorrelationBuffer.java b/src/main/java/pulse/tasks/processing/CorrelationBuffer.java index fc0c0f7..9a79788 100644 --- a/src/main/java/pulse/tasks/processing/CorrelationBuffer.java +++ b/src/main/java/pulse/tasks/processing/CorrelationBuffer.java @@ -1,5 +1,6 @@ package pulse.tasks.processing; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -18,14 +19,15 @@ import pulse.util.ImmutableDataEntry; import pulse.util.ImmutablePair; -public class CorrelationBuffer { +public class CorrelationBuffer implements Serializable { - private final List params; + private static final long serialVersionUID = 1672281370094463238L; + private List params; private static final Set> excludePairList; private static final Set excludeSingleList; private final static double DEFAULT_THRESHOLD = 1E-3; - + static { excludePairList = new HashSet<>(); excludeSingleList = new HashSet<>(); @@ -48,27 +50,28 @@ public void inflate(SearchTask t) { public void clear() { params.clear(); } - + /** * Truncates the buffer by excluding nearly-converged results. */ - private void truncate(double threshold) { int i = 0; int size = params.size(); - final double thresholdSq = threshold*threshold; - - for(i = 0; i < size - 1; i = i + 2) { - + final double thresholdSq = threshold * threshold; + + for (i = 0; i < size - 1; i = i + 2) { + Vector vParams = params.get(i).toVector(); Vector vPlusOneParams = params.get(i + 1).toVector(); Vector vDiff = vPlusOneParams.subtract(vParams); - if(vDiff.lengthSq()/vParams.lengthSq() < thresholdSq) - break; + if (vDiff.lengthSq() / vParams.lengthSq() < thresholdSq) { + break; + } + } + + for (int j = size - 1; j > i; j--) { + params.remove(j); } - - for(int j = size - 1; j > i; j--) - params.remove(j); } public Map, Double> evaluate(CorrelationTest t) { @@ -81,12 +84,12 @@ public Map, Double> evaluate(CorrelationTest } truncate(DEFAULT_THRESHOLD); - + List indices = params.get(0).getParameters().stream() .map(ps -> ps.getIdentifier()).collect(Collectors.toList()); Map map = indices.stream() .map(index -> new ImmutableDataEntry<>(index, params.stream().mapToDouble( - v -> v.getParameterValue(index.getKeyword(), index.getIndex())).toArray())) + v -> v.getParameterValue(index.getKeyword(), index.getIndex())).toArray())) .collect(Collectors.toMap(x -> x.getKey(), x -> x.getValue())); int indicesSize = indices.size(); @@ -96,26 +99,26 @@ public Map, Double> evaluate(CorrelationTest for (int i = 0; i < indicesSize; i++) { var iKey = indices.get(i).getKeyword(); - - if (!excludeSingleList.contains(iKey)) { - + + if (!excludeSingleList.contains(iKey)) { + for (int j = i + 1; j < indicesSize; j++) { - + var jKey = indices.get(j).getKeyword(); - + pair = new ImmutablePair<>(iKey, jKey); - - if (!excludeSingleList.contains(jKey) - && !excludePairList.contains(pair)) { - - correlationMap.put( - new ImmutablePair<>(indices.get(i), indices.get(j)), - t.evaluate(map.get(indices.get(i)), map.get(indices.get(j)))); - + + if (!excludeSingleList.contains(jKey) + && !excludePairList.contains(pair)) { + + correlationMap.put( + new ImmutablePair<>(indices.get(i), indices.get(j)), + t.evaluate(map.get(indices.get(i)), map.get(indices.get(j)))); + } - + } - + } } @@ -132,7 +135,7 @@ public boolean test(CorrelationTest t) { } var values = map.values(); - + return map.values().stream().anyMatch(d -> t.compareToThreshold(d)); } diff --git a/src/main/java/pulse/tasks/processing/Result.java b/src/main/java/pulse/tasks/processing/Result.java index 626e292..083b82f 100644 --- a/src/main/java/pulse/tasks/processing/Result.java +++ b/src/main/java/pulse/tasks/processing/Result.java @@ -1,6 +1,7 @@ package pulse.tasks.processing; import pulse.tasks.Calculation; +import pulse.tasks.Identifier; import pulse.tasks.SearchTask; import pulse.ui.Messages; @@ -13,6 +14,12 @@ */ public class Result extends AbstractResult { + /** + * + */ + private static final long serialVersionUID = 471531411060979791L; + private Identifier id; + /** * Creates an individual {@code Result} related to the current state of * {@code task} using the specified {@code format}. @@ -29,14 +36,19 @@ public Result(SearchTask task, ResultFormat format) throws IllegalArgumentExcept throw new IllegalArgumentException(Messages.getString("Result.NullTaskError")); } - setParent((Calculation)task.getResponse()); + id = task.getIdentifier(); + setParent((Calculation) task.getResponse()); format.getKeywords().stream().forEach(key -> addProperty(task.numericProperty(key))); - } public Result(Result r) { super(r); + id = r.getTaskIdentifier(); + } + + public Identifier getTaskIdentifier() { + return id; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/processing/ResultFormat.java b/src/main/java/pulse/tasks/processing/ResultFormat.java index 6611a84..3c0cd56 100644 --- a/src/main/java/pulse/tasks/processing/ResultFormat.java +++ b/src/main/java/pulse/tasks/processing/ResultFormat.java @@ -1,5 +1,6 @@ package pulse.tasks.processing; +import java.io.Serializable; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static pulse.properties.NumericProperties.def; @@ -11,6 +12,7 @@ import java.util.List; import pulse.properties.NumericPropertyKeyword; +import pulse.tasks.TaskManager; import pulse.tasks.listeners.ResultFormatEvent; import pulse.tasks.listeners.ResultFormatListener; @@ -22,7 +24,12 @@ * characters. *

*/ -public class ResultFormat { +public class ResultFormat implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -3155104011585735097L; private List nameMap; @@ -45,15 +52,22 @@ private ResultFormat() { private ResultFormat(List keys) { nameMap = new ArrayList<>(); - keys.forEach(key -> - nameMap.add(key) + keys.forEach(key + -> nameMap.add(key) ); + TaskManager.addSessionListener(() -> format = this); } - + public static void addResultFormatListener(ResultFormatListener rfl) { listeners.add(rfl); } + public static void removeListeners() { + if (listeners != null) { + listeners.clear(); + } + } + public static ResultFormat generateFormat(List keys) { format = new ResultFormat(keys); @@ -161,4 +175,4 @@ public boolean equals(Object o) { } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/tasks/processing/ResultStatistics.java b/src/main/java/pulse/tasks/processing/ResultStatistics.java index 807b2f5..d6d0911 100644 --- a/src/main/java/pulse/tasks/processing/ResultStatistics.java +++ b/src/main/java/pulse/tasks/processing/ResultStatistics.java @@ -1,5 +1,6 @@ package pulse.tasks.processing; +import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -16,14 +17,15 @@ * * @author Artem Lunev */ -class ResultStatistics { +class ResultStatistics implements Serializable { + private static final long serialVersionUID = 4617029204359661289L; private double[] av; private double[] err; /** * Confidence level of {@value CONFIDENCE_LEVEL} for error calculation using - * t-distribution quantiles. + * t-distribution quantiles. */ public final static double CONFIDENCE_LEVEL = 0.95; @@ -41,8 +43,9 @@ public ResultStatistics() { * assuming a {@value CONFIDENCE_LEVEL} confidence level, by calculating a * standard deviation for each {@code NumericPropertyKeyword} and * multiplying the result by the quantile value of the - * t-distribution. The inverse cumulative distribution function of - * Student distribution is calculated using {@code ApacheCommonsMath} library. + * t-distribution. The inverse cumulative distribution function of + * Student distribution is calculated using {@code ApacheCommonsMath} + * library. * * @param results a list of individual (or average) results to be processed */ @@ -60,23 +63,21 @@ public void process(List results) { The number of elements in the parameter list. This ASSUMES that the input list contains results with the same number of output parameters! */ - StandardDeviation sd = new StandardDeviation(true); //bias-corrected sd double sqrtn = Math.sqrt(results.size()); //calculate average values - var stats = ResultFormat.getInstance().getKeywords().stream() - .map(key -> map.get(key)) //preserve the original order of keywods - .map(c -> { - double mean = openStream(c).average().orElse(0.0); //fail-safe, in case if avg is undefined - return new ImmutablePair( - mean, //the mean value - sd.evaluate(openStream(c).toArray(), mean) //that would be the sample standard deviation - / sqrtn //however, since we are calculating the std of the MEAN, - //we need to divide the result by sqrtn - ); - }).collect(Collectors.toList()); + .map(key -> map.get(key)) //preserve the original order of keywods + .map(c -> { + double mean = openStream(c).average().orElse(0.0); //fail-safe, in case if avg is undefined + return new ImmutablePair( + mean, //the mean value + sd.evaluate(openStream(c).toArray(), mean) //that would be the sample standard deviation + / sqrtn //however, since we are calculating the std of the MEAN, + //we need to divide the result by sqrtn + ); + }).collect(Collectors.toList()); av = stats.stream().mapToDouble(pair -> pair.getFirst()).toArray(); //store mean values @@ -90,7 +91,7 @@ public void process(List results) { ).toArray(); //store errors } - + private DoubleStream openStream(List input) { return input.stream().mapToDouble(d -> d); } diff --git a/src/main/java/pulse/ui/ColorGenerator.java b/src/main/java/pulse/ui/ColorGenerator.java new file mode 100644 index 0000000..ac56b59 --- /dev/null +++ b/src/main/java/pulse/ui/ColorGenerator.java @@ -0,0 +1,44 @@ +package pulse.ui; + +import java.awt.Color; +import static java.awt.Color.BLUE; +import static java.awt.Color.GREEN; +import static java.awt.Color.RED; +import java.util.ArrayList; + +public class ColorGenerator { + + private Color a, b, c; + + public ColorGenerator() { + a = RED; + b = GREEN; + c = BLUE; + } + + public Color[] random(int number) { + var list = new ArrayList(); + for (int i = 0; i < number; i++) { + list.add(sample(i / (double) (number - 1))); + } + //Collections.shuffle(list); + return list.toArray(new Color[list.size()]); + } + + public Color sample(double seed) { + return seed < 0.5 + ? mix(a, b, (float) (seed * 2)) + : mix(b, c, (float) ((seed - 0.5) * 2)); + } + + private static Color mix(Color a, Color b, float ratio) { + float[] aRgb = a.getRGBComponents(null); + float[] bRgb = b.getRGBComponents(null); + float[] cRgb = new float[3]; + for (int i = 0; i < cRgb.length; i++) { + cRgb[i] = aRgb[i] * (1.0f - ratio) + bRgb[i] * ratio; + } + return new Color(cRgb[0], cRgb[1], cRgb[2]); + } + +} diff --git a/src/main/java/pulse/ui/Launcher.java b/src/main/java/pulse/ui/Launcher.java index de97832..3744e9e 100644 --- a/src/main/java/pulse/ui/Launcher.java +++ b/src/main/java/pulse/ui/Launcher.java @@ -17,11 +17,11 @@ import javax.swing.JOptionPane; import javax.swing.UIManager; -import com.alee.laf.WebLookAndFeel; -import com.alee.skin.dark.WebDarkSkin; +import com.formdev.flatlaf.FlatDarkLaf; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; +import pulse.util.Serializer; /** *

@@ -52,7 +52,7 @@ private Launcher() { */ public static void main(String[] args) { new Launcher(); - + if (!LOCK.exists()) { try { @@ -60,14 +60,15 @@ public static void main(String[] args) { } catch (IOException ex) { Logger.getLogger(Launcher.class.getName()).log(Level.SEVERE, "Unable to create lock file", ex); } - + LOCK.deleteOnExit(); splashScreen(); - WebLookAndFeel.install(WebDarkSkin.class); + //WebLookAndFeel.install(WebDarkSkin.class); + FlatDarkLaf.setup(); try { - UIManager.setLookAndFeel(new WebLookAndFeel()); + UIManager.setLookAndFeel(new FlatDarkLaf()); } catch (Exception ex) { System.err.println("Failed to initialize LaF"); } @@ -148,7 +149,7 @@ private void createShutdownHook() { errorLog.delete(); } //delete lock explicitly on abnormal termination - if(LOCK.exists()) { + if (LOCK.exists()) { LOCK.delete(); } }; diff --git a/src/main/java/pulse/ui/components/AuxPlotter.java b/src/main/java/pulse/ui/components/AuxPlotter.java index 8150c4c..f35d34d 100644 --- a/src/main/java/pulse/ui/components/AuxPlotter.java +++ b/src/main/java/pulse/ui/components/AuxPlotter.java @@ -1,11 +1,16 @@ package pulse.ui.components; +import java.awt.Color; import java.awt.Font; +import javax.swing.JLabel; import javax.swing.UIManager; +import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.CombinedDomainXYPlot; +import static org.jfree.chart.plot.PlotOrientation.VERTICAL; import org.jfree.chart.plot.XYPlot; public abstract class AuxPlotter { @@ -14,45 +19,67 @@ public abstract class AuxPlotter { private JFreeChart chart; private XYPlot plot; - public AuxPlotter(String xLabel, String yLabel) { - createChart(xLabel, yLabel); - chart.setBackgroundPaint(UIManager.getColor("Panel.background")); + public AuxPlotter() { + //empty + } - plot = chart.getXYPlot(); - setFonts(); + public AuxPlotter(String xLabel, String yLabel) { + setChart(ChartFactory.createScatterPlot("", xLabel, yLabel, null, VERTICAL, true, true, false)); + setPlot(chart.getXYPlot()); chart.removeLegend(); - chartPanel = new ChartPanel(chart); + + setFonts(); } - public void setFonts() { - var fontLabel = new Font("Arial", Font.PLAIN, 20); - var fontTicks = new Font("Arial", Font.PLAIN, 16); - var plot = getPlot(); - plot.getDomainAxis().setLabelFont(fontLabel); - plot.getDomainAxis().setTickLabelFont(fontTicks); - plot.getRangeAxis().setLabelFont(fontLabel); - plot.getRangeAxis().setTickLabelFont(fontTicks); + public final void setFonts() { + var jlabel = new JLabel(); + var label = jlabel.getFont().deriveFont(20f); + var ticks = jlabel.getFont().deriveFont(16f); + chart.getTitle().setFont(jlabel.getFont().deriveFont(20f)); + + if (plot instanceof CombinedDomainXYPlot) { + var combinedPlot = (CombinedDomainXYPlot) plot; + combinedPlot.getSubplots().stream().forEach(sp -> setFontsForPlot((XYPlot) sp, label, ticks)); + } else { + setFontsForPlot(plot, label, ticks); + } + } - public abstract void createChart(String xLabel, String yLabel); + private void setFontsForPlot(XYPlot p, Font label, Font ticks) { + var foreColor = UIManager.getColor("Label.foreground"); + var domainAxis = p.getDomainAxis(); + Chart.setAxisFontColor(domainAxis, foreColor); + var rangeAxis = p.getRangeAxis(); + Chart.setAxisFontColor(rangeAxis, foreColor); + } public abstract void plot(T t); - public ChartPanel getChartPanel() { + public final ChartPanel getChartPanel() { return chartPanel; } - public JFreeChart getChart() { + public final JFreeChart getChart() { return chart; } - public XYPlot getPlot() { + public final XYPlot getPlot() { return plot; } - public void setChart(JFreeChart chart) { + public final void setPlot(XYPlot plot) { + this.plot = plot; + plot.setBackgroundPaint(chart.getBackgroundPaint()); + } + + public final void setChart(JFreeChart chart) { this.chart = chart; + var color = UIManager.getLookAndFeelDefaults().getColor("TextPane.background"); + chart.setBackgroundPaint(color); + chartPanel = new ChartPanel(chart); + this.plot = chart.getXYPlot(); } } diff --git a/src/main/java/pulse/ui/components/CalculationTable.java b/src/main/java/pulse/ui/components/CalculationTable.java index 95c4ab1..4ebf483 100644 --- a/src/main/java/pulse/ui/components/CalculationTable.java +++ b/src/main/java/pulse/ui/components/CalculationTable.java @@ -8,6 +8,7 @@ import java.util.concurrent.Executors; import javax.swing.JTable; +import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.table.TableCellRenderer; @@ -50,10 +51,7 @@ public CalculationTable() { var instance = TaskManager.getManagerInstance(); instance.addTaskRepositoryListener(e -> { - if (e.getState() == TaskRepositoryEvent.State.TASK_MODEL_SWITCH) { - var t = instance.getTask(e.getId()); - identifySelection(t); - } else if (e.getState() == TaskRepositoryEvent.State.TASK_CRITERION_SWITCH) { + if (e.getState() == TaskRepositoryEvent.State.TASK_CRITERION_SWITCH) { update(TaskManager.getManagerInstance().getSelectedTask()); } @@ -84,7 +82,9 @@ public void initListeners() { lsm.addListSelectionListener((ListSelectionEvent e) -> { var task = TaskManager.getManagerInstance().getSelectedTask(); var id = convertRowIndexToModel(this.getSelectedRow()); - if (!lsm.getValueIsAdjusting() && id > -1 && id < task.getStoredCalculations().size()) { + if (!lsm.getValueIsAdjusting() + && id > -1 + && id < task.getStoredCalculations().size()) { plotExecutor.submit(() -> { task.switchTo(task.getStoredCalculations().get(id)); diff --git a/src/main/java/pulse/ui/components/Chart.java b/src/main/java/pulse/ui/components/Chart.java index 68dda2d..0801ac8 100644 --- a/src/main/java/pulse/ui/components/Chart.java +++ b/src/main/java/pulse/ui/components/Chart.java @@ -19,6 +19,7 @@ import java.awt.Color; import java.awt.Font; import java.awt.event.MouseEvent; +import java.io.Serializable; import javax.swing.SwingUtilities; import javax.swing.UIManager; @@ -26,6 +27,7 @@ import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.annotations.XYTitleAnnotation; +import org.jfree.chart.axis.Axis; import org.jfree.chart.block.BlockBorder; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; @@ -51,10 +53,10 @@ import pulse.tasks.listeners.TaskRepositoryEvent; import pulse.ui.components.listeners.MouseOnMarkerListener; -public class Chart { +public class Chart implements Serializable { - private ChartPanel chartPanel; - private JFreeChart chart; + private final ChartPanel chartPanel; + private final JFreeChart chart; private XYPlot plot; private float opacity = 0.15f; @@ -81,7 +83,7 @@ public Chart() { final TaskManager instance = TaskManager.getManagerInstance(); chart.removeLegend(); - chart.setBackgroundPaint(UIManager.getColor("Panel.background")); + chart.setBackgroundPaint(UIManager.getColor("TextPane.background")); chartPanel = new ChartPanel(chart) { @Override @@ -146,16 +148,18 @@ public double xCoord(MouseEvent e) { } private void setFonts() { - var fontLabel = new Font("Arial", Font.PLAIN, 20); - var fontTicks = new Font("Arial", Font.PLAIN, 14); - plot.getDomainAxis().setLabelFont(fontLabel); - plot.getDomainAxis().setTickLabelFont(fontTicks); - plot.getRangeAxis().setLabelFont(fontLabel); - plot.getRangeAxis().setTickLabelFont(fontTicks); + var foreColor = UIManager.getColor("Label.foreground"); + setAxisFontColor(plot.getDomainAxis(), foreColor); + setAxisFontColor(plot.getRangeAxis(), foreColor); + } + + public static void setAxisFontColor(Axis axis, Color color) { + axis.setLabelPaint(color); + axis.setTickLabelPaint(color); } private void setBackgroundAndGrid() { - // plot.setBackgroundPaint(UIManager.getColor("Panel.background")); + plot.setBackgroundPaint(UIManager.getColor("TextPane.background")); plot.setRangeGridlinesVisible(true); plot.setRangeGridlinePaint(GRAY); diff --git a/src/main/java/pulse/ui/components/DataLoader.java b/src/main/java/pulse/ui/components/DataLoader.java index 34f9f97..2baa814 100644 --- a/src/main/java/pulse/ui/components/DataLoader.java +++ b/src/main/java/pulse/ui/components/DataLoader.java @@ -9,7 +9,6 @@ import java.net.URISyntaxException; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.concurrent.Executors; import javax.swing.JFileChooser; @@ -17,8 +16,6 @@ import javax.swing.filechooser.FileNameExtensionFilter; import pulse.input.ExperimentalData; -import pulse.input.InterpolationDataset; -import pulse.input.InterpolationDataset.StandartType; import pulse.io.readers.MetaFilePopulator; import pulse.io.readers.ReaderManager; import pulse.problem.laser.NumericPulse; @@ -111,7 +108,7 @@ public static void loadMetadataDialog() { e.printStackTrace(); } - var p = ( (Calculation) task.getResponse() ).getProblem(); + var p = ((Calculation) task.getResponse()).getProblem(); if (p != null) { p.retrieveData(data); } @@ -175,14 +172,25 @@ public static void loadPulseDialog() { * * @param f a {@code File} containing a property specified by the * {@code type} - * @param type the type of the loaded data * @throws IOException if file cannot be read * @see pulse.tasks.TaskManager.evaluate() */ - public static void load(StandartType type, File f) throws IOException { - Objects.requireNonNull(f); - InterpolationDataset.setDataset(read(datasetReaders(), f), type); - TaskManager.getManagerInstance().evaluate(); + public static void loadDensity(File f) throws IOException { + TaskManager.getManagerInstance().setDensityDataset(read(datasetReaders(), f)); + } + + /** + * Uses the {@code ReaderManager} to create an {@code InterpolationDataset} + * from data stored in {@code f} and updates the associated properties of + * each task. + * + * @param f a {@code File} containing a property specified by the + * {@code type} + * @throws IOException if file cannot be read + * @see pulse.tasks.TaskManager.evaluate() + */ + public static void loadSpecificHeat(File f) throws IOException { + TaskManager.getManagerInstance().setSpecificHeatDataset(read(datasetReaders(), f)); } private static List userInput(String descriptor, List extensions) { diff --git a/src/main/java/pulse/ui/components/GraphicalLogPane.java b/src/main/java/pulse/ui/components/GraphicalLogPane.java new file mode 100644 index 0000000..1b643f5 --- /dev/null +++ b/src/main/java/pulse/ui/components/GraphicalLogPane.java @@ -0,0 +1,100 @@ +package pulse.ui.components; + +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import static pulse.properties.NumericPropertyKeyword.ITERATION; +import pulse.tasks.TaskManager; +import pulse.tasks.listeners.TaskRepositoryEvent; +import pulse.tasks.logs.AbstractLogger; +import pulse.tasks.logs.DataLogEntry; +import pulse.tasks.logs.Log; +import pulse.tasks.logs.LogEntry; +import static pulse.tasks.logs.Status.DONE; + +@SuppressWarnings("serial") +public class GraphicalLogPane extends AbstractLogger { + + private final LogChart chart; + private final JScrollPane pane; + + public GraphicalLogPane() { + pane = new JScrollPane(); + pane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + chart = new LogChart(); + pane.setViewportView(chart.getChartPanel()); + TaskManager.getManagerInstance().addTaskRepositoryListener(e -> { + if (e.getState() == TaskRepositoryEvent.State.TASK_SUBMITTED) { + chart.clear(); + } + }); + } + + @Override + public JComponent getGUIComponent() { + return pane; + } + + @Override + public void printTimeTaken(Log log) { + long[] time = log.timeTaken(); + StringBuilder sb = new StringBuilder("Finished in "); + sb.append(time[0]).append(" s ").append(time[1]).append(" ms."); + } + + @Override + public void post(LogEntry logEntry) { + if (logEntry instanceof DataLogEntry) { + var dle = (DataLogEntry) logEntry; + double iteration = dle.getData().stream() + .filter(p -> p.getIdentifier().getKeyword() == ITERATION) + .findAny().get().getApparentValue(); + + chart.changeAxis(true); + chart.plot((DataLogEntry) logEntry, iteration); + + } + } + + @Override + public void postAll() { + clear(); + + var task = TaskManager.getManagerInstance().getSelectedTask(); + + if (task != null) { + + var log = task.getLog(); + + if (log.isStarted() && log.isFinished()) { + + chart.clear(); + chart.changeAxis(false); + chart.plot(log); + + if (task.getStatus() == DONE) { + printTimeTaken(log); + } + + } + + } + + } + + @Override + public void post(String text) { + //not supported + } + + @Override + public void clear() { + chart.clear(); + } + + @Override + public boolean isEmpty() { + return false; + } + +} diff --git a/src/main/java/pulse/ui/components/LogChart.java b/src/main/java/pulse/ui/components/LogChart.java new file mode 100644 index 0000000..f1c896c --- /dev/null +++ b/src/main/java/pulse/ui/components/LogChart.java @@ -0,0 +1,222 @@ +package pulse.ui.components; + +import static java.util.Objects.requireNonNull; + +import java.awt.BasicStroke; +import java.awt.Color; +import static java.awt.Color.WHITE; +import static java.awt.Color.black; +import java.awt.Dimension; +import java.time.Duration; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.swing.SwingUtilities; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.annotations.XYTitleAnnotation; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.axis.NumberTickUnit; +import org.jfree.chart.block.BlockBorder; +import org.jfree.chart.plot.CombinedDomainXYPlot; +import org.jfree.chart.plot.Plot; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.title.LegendTitle; +import org.jfree.chart.ui.RectangleAnchor; +import org.jfree.chart.ui.RectangleEdge; +import org.jfree.data.Range; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; +import pulse.Response; +import pulse.math.ParameterIdentifier; + +import static pulse.properties.NumericPropertyKeyword.ITERATION; +import pulse.tasks.SearchTask; +import pulse.tasks.TaskManager; +import pulse.tasks.logs.DataLogEntry; +import pulse.tasks.logs.Log; +import pulse.tasks.logs.StateEntry; +import pulse.tasks.logs.Status; +import pulse.tasks.processing.Buffer; +import pulse.ui.ColorGenerator; + +public class LogChart extends AuxPlotter { + + private final Map plots; + private Color[] colors; + private static final ColorGenerator cg = new ColorGenerator(); + public final static int HEIGHT_FACTOR = 75; + public final static int MARGIN = 10; + + public LogChart() { + var plot = new CombinedDomainXYPlot(new NumberAxis("Iteration")); + plot.setGap(10.0); + plot.setOrientation(PlotOrientation.VERTICAL); + var chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, true); + setChart(chart); + plots = new HashMap<>(); + getChart().removeLegend(); + } + + public final void clear() { + var p = (CombinedDomainXYPlot) getPlot(); + if (p != null) { + if (p.getDomainAxis() != null) { + p.getDomainAxis().setAutoRange(true); + } + plots.values().stream().forEach(pp -> p.remove(pp)); + } + plots.clear(); + colors = new Color[0]; + } + + private void setLegendTitle(Plot plot) { + var lt = new LegendTitle(plot); + lt.setBackgroundPaint(new Color(200, 200, 255, 100)); + lt.setFrame(new BlockBorder(black)); + lt.setPosition(RectangleEdge.RIGHT); + var ta = new XYTitleAnnotation(0.0, 0.8, lt, RectangleAnchor.LEFT); + ta.setMaxWidth(0.58); + ((XYPlot) plot).addAnnotation(ta); + } + + public final void add(ParameterIdentifier key, int no) { + var plot = new XYPlot(); + var axis = new NumberAxis(); + axis.setAutoRangeIncludesZero(false); + plot.setRangeAxis(axis); + + plot.setBackgroundPaint(getChart().getBackgroundPaint()); + + plots.put(key, plot); + ((CombinedDomainXYPlot) getPlot()).add(plot); + + int height = HEIGHT_FACTOR * plots.size(); + int width = getChartPanel().getParent().getWidth() - MARGIN; + getChartPanel().setPreferredSize(new Dimension(width, height)); + getChartPanel().revalidate(); + + var dataset = new XYSeriesCollection(); + var series = new XYSeries(key.toString()); + + dataset.addSeries(series); + dataset.addSeries(new XYSeries("Running average")); + plot.setDataset(dataset); + setLegendTitle(plot); + + setRenderer(plot, colors[no]); + setFonts(); + } + + private void setRenderer(XYPlot plt, Color clr) { + var renderer = new XYLineAndShapeRenderer(true, false); + renderer.setSeriesPaint(0, clr); + renderer.setSeriesPaint(1, WHITE); + var dashed = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, 10.0f, new float[]{10.0f}, 0.0f); + renderer.setSeriesStroke(1, dashed); + renderer.setSeriesVisibleInLegend(1, Boolean.FALSE); + plt.setRenderer(renderer); + } + + public void changeAxis(boolean iterationMode) { + var domainAxis = (NumberAxis) getPlot().getDomainAxis(); + domainAxis.setLabel(iterationMode ? "Iteration" : "Time (ms)"); + domainAxis.setAutoRange(!iterationMode); + if (iterationMode) { + domainAxis.setTickUnit(new NumberTickUnit(1)); + } else { + domainAxis.setAutoTickUnitSelection(true); + } + } + + @Override + public void plot(Log l) { + requireNonNull(l); + + List startTimes = l.getLogEntries().stream() + .filter(le -> le instanceof DataLogEntry && le.getPreviousEntry() instanceof StateEntry) + .map(entry -> entry.getTime()).collect(Collectors.toList()); + + if (!startTimes.isEmpty()) { + var recentStart = startTimes.get(startTimes.size() - 1); + l.getLogEntries().stream().filter(le -> le.getTime().isAfter(recentStart)) + .filter(e -> e instanceof DataLogEntry).forEach(dle + -> plot((DataLogEntry) dle, + Duration.between(recentStart, dle.getTime()).toMillis())); + } + + } + + private static void adjustRange(XYPlot pl, int iteration, int bufSize) { + int lower = (iteration / bufSize) * bufSize; + + var domainAxis = pl.getDomainAxis(); + if (domainAxis != null) { + var r = domainAxis.getRange(); + var newR = new Range(lower, lower + bufSize); + + if (!r.equals(newR) && iteration > lower) { + ((XYPlot) pl).getDomainAxis().setRange(lower, lower + bufSize); + } + } + } + + public final void plot(DataLogEntry dle, double iterationOrTime) { + requireNonNull(dle); + + var data = dle.getData(); + int size = data.size(); + + if (colors == null || colors.length < size) { + colors = cg.random(size - 1); + } + + SearchTask task = TaskManager.getManagerInstance().getTask(dle.getIdentifier()); + Buffer buf = task.getBuffer(); + final int bufSize = buf.getData().length; + + for (int i = 0, j = 0; i < size; i++) { + var p = data.get(i); + var np = p.getIdentifier(); + + if (np.getKeyword() == ITERATION) { + continue; + } + + double value = p.getApparentValue(); + + if (!plots.containsKey(np)) { + add(np, j++); + } + + Plot pl = plots.get(np); + + var dataset = (XYSeriesCollection) ((XYPlot) pl).getDataset(); + XYSeries series = (XYSeries) dataset.getSeries(0); + series.add(iterationOrTime, value); + + if (task.getStatus() == Status.IN_PROGRESS) { + + XYSeries runningAverage = dataset.getSeries(1); + if (iterationOrTime > buf.getData().length - 1) { + runningAverage.add(iterationOrTime, buf.average(np.getKeyword())); + } + + SwingUtilities.invokeLater(() -> adjustRange((XYPlot) pl, (int) iterationOrTime, bufSize)); + + } else { + var domainAxis = ((XYPlot) pl).getDomainAxis(); + domainAxis.setAutoRange(true); + } + + } + + } + +} diff --git a/src/main/java/pulse/ui/components/LogPane.java b/src/main/java/pulse/ui/components/LogPane.java deleted file mode 100644 index 5af4215..0000000 --- a/src/main/java/pulse/ui/components/LogPane.java +++ /dev/null @@ -1,136 +0,0 @@ -package pulse.ui.components; - -import static java.lang.System.err; -import static java.time.temporal.ChronoUnit.MILLIS; -import static java.time.temporal.ChronoUnit.SECONDS; -import static java.util.concurrent.Executors.newSingleThreadExecutor; -import static javax.swing.text.DefaultCaret.ALWAYS_UPDATE; -import static pulse.tasks.logs.Status.DONE; -import static pulse.ui.Messages.getString; - -import java.io.IOException; -import java.util.concurrent.ExecutorService; - -import javax.swing.JEditorPane; -import javax.swing.text.BadLocationException; -import javax.swing.text.DefaultCaret; -import javax.swing.text.html.HTMLDocument; -import javax.swing.text.html.HTMLEditorKit; - -import pulse.tasks.TaskManager; -import pulse.tasks.logs.Log; -import pulse.tasks.logs.LogEntry; -import pulse.util.Descriptive; - -@SuppressWarnings("serial") -public class LogPane extends JEditorPane implements Descriptive { - - private ExecutorService updateExecutor = newSingleThreadExecutor(); - - public LogPane() { - super(); - setContentType("text/html"); - setEditable(false); - var c = (DefaultCaret) getCaret(); - c.setUpdatePolicy(ALWAYS_UPDATE); - } - - private void post(LogEntry logEntry) { - post(logEntry.toString()); - } - - /* - private void postError(String text) { - var sb = new StringBuilder(); - sb.append(getString("DataLogEntry.FontTagError")); - sb.append(text); - sb.append(getString("DataLogEntry.FontTagClose")); - post(sb.toString()); - }*/ - private void post(String text) { - - final var doc = (HTMLDocument) getDocument(); - final var kit = (HTMLEditorKit) this.getEditorKit(); - try { - kit.insertHTML(doc, doc.getLength(), text, 0, 0, null); - } catch (BadLocationException e) { - err.println(getString("LogPane.InsertError")); //$NON-NLS-1$ - e.printStackTrace(); - } catch (IOException e) { - err.println(getString("LogPane.PrintError")); //$NON-NLS-1$ - e.printStackTrace(); - } - - } - - public void printTimeTaken(Log log) { - var seconds = SECONDS.between(log.getStart(), log.getEnd()); - var ms = MILLIS.between(log.getStart(), log.getEnd()) - 1000L * seconds; - var sb = new StringBuilder(); - sb.append(getString("LogPane.TimeTaken")); //$NON-NLS-1$ - sb.append(seconds + getString("LogPane.Seconds")); //$NON-NLS-1$ - sb.append(ms + getString("LogPane.Milliseconds")); //$NON-NLS-1$ - post(sb.toString()); - } - - public synchronized void callUpdate() { - updateExecutor.submit(() -> update()); - } - - public void printAll() { - clear(); - - var task = TaskManager.getManagerInstance().getSelectedTask(); - - if (task != null) { - - var log = task.getLog(); - - if (log.isStarted()) { - - log.getLogEntries().stream().forEach(entry -> post(entry)); - - if (task.getStatus() == DONE) { - printTimeTaken(log); - } - - } - - } - - } - - private synchronized void update() { - var task = TaskManager.getManagerInstance().getSelectedTask(); - - if (task == null) { - return; - } - - var log = task.getLog(); - - if (!log.isStarted()) { - return; - } - - post(log.lastEntry()); - } - - public void clear() { - try { - getDocument().remove(0, getDocument().getLength()); - } catch (BadLocationException e) { - e.printStackTrace(); - } - } - - public ExecutorService getUpdateExecutor() { - return updateExecutor; - } - - @Override - public String describe() { - return "Log_" + TaskManager.getManagerInstance().getSelectedTask().getIdentifier().getValue(); - } - -} diff --git a/src/main/java/pulse/ui/components/ProblemTree.java b/src/main/java/pulse/ui/components/ProblemTree.java index e4c2e0d..0ac58e5 100644 --- a/src/main/java/pulse/ui/components/ProblemTree.java +++ b/src/main/java/pulse/ui/components/ProblemTree.java @@ -67,7 +67,7 @@ private void addListeners() { }); instance.addSelectionListener(e -> { - var current = ( (Calculation) instance.getSelectedTask().getResponse() ).getProblem(); + var current = ((Calculation) instance.getSelectedTask().getResponse()).getProblem(); // select appropriate problem type from list setSelectedProblem(current); diff --git a/src/main/java/pulse/ui/components/PropertyHolderTable.java b/src/main/java/pulse/ui/components/PropertyHolderTable.java index 05413b6..2157789 100644 --- a/src/main/java/pulse/ui/components/PropertyHolderTable.java +++ b/src/main/java/pulse/ui/components/PropertyHolderTable.java @@ -155,11 +155,6 @@ public TableCellEditor getCellEditor(int row, int column) { return new DefaultCellEditor((JComboBox) value); } - if (value instanceof Enum) { - return new DefaultCellEditor( - new JComboBox(((Enum) value).getDeclaringClass().getEnumConstants())); - } - if (value instanceof InstanceDescriptor) { return new InstanceCellEditor((InstanceDescriptor) value); } @@ -187,7 +182,7 @@ public TableCellEditor getCellEditor(int row, int column) { value, false, false, row, column), ((Flag) value).getType()); } - return getDefaultEditor(value.getClass()); + return super.getCellEditor(row, column); } diff --git a/src/main/java/pulse/ui/components/PulseChart.java b/src/main/java/pulse/ui/components/PulseChart.java index bb33646..b3c7d06 100644 --- a/src/main/java/pulse/ui/components/PulseChart.java +++ b/src/main/java/pulse/ui/components/PulseChart.java @@ -4,13 +4,11 @@ import static java.awt.Color.black; import static java.awt.Font.PLAIN; import static java.util.Objects.requireNonNull; -import static org.jfree.chart.plot.PlotOrientation.VERTICAL; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; -import org.jfree.chart.ChartFactory; import org.jfree.chart.annotations.XYTitleAnnotation; import org.jfree.chart.block.BlockBorder; import org.jfree.chart.renderer.xy.XYDifferenceRenderer; @@ -41,16 +39,11 @@ private void setRenderer() { getPlot().setRenderer(rendererPulse); } - @Override - public void createChart(String xLabel, String yLabel) { - setChart(ChartFactory.createScatterPlot("", xLabel, yLabel, null, VERTICAL, true, true, false)); - } - private void setLegendTitle() { var plot = getPlot(); var lt = new LegendTitle(plot); lt.setItemFont(new Font("Dialog", PLAIN, 16)); - //lt.setBackgroundPaint(new Color(200, 200, 255, 100)); + lt.setBackgroundPaint(new Color(200, 200, 255, 100)); lt.setFrame(new BlockBorder(black)); lt.setPosition(RectangleEdge.RIGHT); var ta = new XYTitleAnnotation(0.5, 0.2, lt, RectangleAnchor.CENTER); @@ -67,9 +60,9 @@ public void plot(Calculation c) { double startTime = (double) problem.getHeatingCurve().getTimeShift().getValue(); var pulseDataset = new XYSeriesCollection(); - - pulseDataset.addSeries(series(problem.getPulse(), c.getScheme().getGrid().getTimeStep(), - problem.getProperties().timeFactor(), startTime)); + + pulseDataset.addSeries(series(problem.getPulse(), c.getScheme().getGrid().getTimeStep() / 20.0, + problem.getProperties().characteristicTime(), startTime)); getPlot().setDataset(0, pulseDataset); } @@ -77,15 +70,15 @@ public void plot(Calculation c) { private static XYSeries series(Pulse pulse, double dx, double timeFactor, double startTime) { var series = new XYSeries(pulse.getPulseShape().toString()); var pulseShape = pulse.getPulseShape(); - + double timeLimit = pulseShape.getPulseWidth(); - double x = startTime/timeFactor; + double x = startTime / timeFactor; - series.add(TO_MILLIS * (startTime - dx * timeFactor / 10.), 0.0); - series.add(TO_MILLIS * (startTime + timeFactor*(timeLimit + dx / 10.)), 0.0); + series.add(TO_MILLIS * (startTime - dx * timeFactor / 100.), 0.0); + series.add(TO_MILLIS * (startTime + timeFactor * (timeLimit + dx / 100.)), 0.0); - for (int i = 0, numPoints = (int) (timeLimit/dx); i < numPoints; i++) { - series.add(x * timeFactor * TO_MILLIS, pulseShape.evaluateAt(x - startTime/timeFactor)); + for (int i = 0, numPoints = (int) (timeLimit / dx); i < numPoints; i++) { + series.add(x * timeFactor * TO_MILLIS, pulseShape.evaluateAt(x - startTime / timeFactor)); x += dx; } diff --git a/src/main/java/pulse/ui/components/PulseMainMenu.java b/src/main/java/pulse/ui/components/PulseMainMenu.java index 216770e..ccf0f89 100644 --- a/src/main/java/pulse/ui/components/PulseMainMenu.java +++ b/src/main/java/pulse/ui/components/PulseMainMenu.java @@ -30,6 +30,8 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.AbstractButton; import javax.swing.ButtonGroup; @@ -39,6 +41,7 @@ import javax.swing.JMenuItem; import javax.swing.JRadioButtonMenuItem; import javax.swing.JSeparator; +import pulse.util.Serializer; import pulse.search.statistics.CorrelationTest; import pulse.search.statistics.NormalityTest; @@ -71,8 +74,8 @@ public class PulseMainMenu extends JMenuBar { private static JMenuItem loadPulseItem; private static JMenuItem modelSettingsItem; - private static ExportDialog exportDialog = new ExportDialog(); - private static FormattedInputDialog bufferDialog = new FormattedInputDialog(def(BUFFER_SIZE)); + private static final ExportDialog exportDialog = new ExportDialog(); + private static final FormattedInputDialog bufferDialog = new FormattedInputDialog(def(BUFFER_SIZE)); private static File dir; @@ -80,24 +83,39 @@ public class PulseMainMenu extends JMenuBar { private List exitListeners; public PulseMainMenu() { - bufferDialog.setConfirmAction(() -> Buffer.setSize(def(BUFFER_SIZE))); + bufferDialog.setConfirmAction(() + -> Buffer.setSize(derive(BUFFER_SIZE, bufferDialog.value()))); initComponents(); initListeners(); assignMenuFunctions(); - addListeners(); + reset(); listeners = new ArrayList<>(); exitListeners = new ArrayList<>(); } - private void addListeners() { - getManagerInstance().addTaskRepositoryListener(event -> { + private void enableIfNeeded() { + var instance = getManagerInstance(); + boolean enabled = instance.getTaskList().size() > 0; + loadMetadataItem.setEnabled(enabled); + loadPulseItem.setEnabled(enabled); + modelSettingsItem.setEnabled(enabled); + searchSettingsItem.setEnabled(enabled); + } + + public final void reset() { + var instance = getManagerInstance(); + + instance.addTaskRepositoryListener(event -> { if (event.getState() == TASK_ADDED) { exportCurrentItem.setEnabled(true); exportAllItem.setEnabled(true); } }); + + enableIfNeeded(); + instance.addTaskRepositoryListener((TaskRepositoryEvent e) -> enableIfNeeded()); } private void initListeners() { @@ -170,7 +188,31 @@ private void initComponents() { fileMenu.add(exportCurrentItem); fileMenu.add(exportAllItem); fileMenu.add(new JSeparator()); + + var serializeItem = new JMenuItem("Save Session..."); + fileMenu.add(serializeItem); + serializeItem.addActionListener(e -> { + try { + Serializer.serialize(); + } catch (IOException | ClassNotFoundException ex) { + Logger.getLogger(PulseMainMenu.class.getName()).log(Level.SEVERE, null, ex); + } + }); + var deserializeItem = new JMenuItem("Load Session..."); + + fileMenu.add(deserializeItem); + deserializeItem.addActionListener(e -> { + + try { + Serializer.deserialize(); + } catch (IOException ex) { + Logger.getLogger(PulseMainMenu.class.getName()).log(Level.SEVERE, null, ex); + } + + }); + fileMenu.add(exitItem); + add(fileMenu); settingsMenu.add(modelSettingsItem); @@ -245,8 +287,8 @@ private JMenu initAnalysisSubmenu() { if (((AbstractButton) e.getItem()).isSelected()) { var text = ((AbstractButton) e.getItem()).getText(); setSelectedOptimiserDescriptor(text); - getManagerInstance().getTaskList().stream().forEach(t -> - ( (Calculation) t.getResponse() ).initOptimiser()); + getManagerInstance().getTaskList().stream().forEach(t + -> ((Calculation) t.getResponse()).initOptimiser()); } }); @@ -274,29 +316,30 @@ private JMenu initAnalysisSubmenu() { JRadioButtonMenuItem corrItem = null; var ct = CorrelationTest.init(); - + for (var corrName : allDescriptors(CorrelationTest.class)) { corrItem = new JRadioButtonMenuItem(corrName); corrItems.add(corrItem); correlationsSubMenu.add(corrItem); - - if(ct.getDescriptor().equalsIgnoreCase(corrName)) + + if (ct.getDescriptor().equalsIgnoreCase(corrName)) { corrItem.setSelected(true); - + } + corrItem.addItemListener(e -> { if (((AbstractButton) e.getItem()).isSelected()) { var text = ((AbstractButton) e.getItem()).getText(); var allTests = Reflexive.instancesOf(CorrelationTest.class); - var optionalTest = allTests.stream().filter(test -> - test.getDescriptor().equalsIgnoreCase(corrName)).findAny(); - - if(optionalTest.isPresent()) { + var optionalTest = allTests.stream().filter(test + -> test.getDescriptor().equalsIgnoreCase(corrName)).findAny(); + + if (optionalTest.isPresent()) { CorrelationTest.getTestDescriptor() .setSelectedDescriptor(optionalTest.get().getClass().getSimpleName()); getManagerInstance().getTaskList().stream().forEach(t -> t.initCorrelationTest()); } - + } }); @@ -336,20 +379,6 @@ private void assignMenuFunctions() { changeDialog.setVisible(true); }); - getManagerInstance().addTaskRepositoryListener((TaskRepositoryEvent e) -> { - if (getManagerInstance().getTaskList().size() > 0) { - loadMetadataItem.setEnabled(true); - loadPulseItem.setEnabled(true);; - modelSettingsItem.setEnabled(true); - searchSettingsItem.setEnabled(true); - } else { - loadMetadataItem.setEnabled(false); - loadPulseItem.setEnabled(false); - modelSettingsItem.setEnabled(false); - searchSettingsItem.setEnabled(false); - } - }); - exportAllItem.setEnabled(true); exportAllItem.addActionListener(e -> { exportDialog.setLocationRelativeTo(null); @@ -369,6 +398,16 @@ private void assignMenuFunctions() { } + public void removeAllListeners() { + if (listeners != null) { + listeners.clear(); + } + if (exitListeners != null) { + exitListeners.clear(); + } + + } + public void addFrameVisibilityRequestListener(FrameVisibilityRequestListener l) { listeners.add(l); } diff --git a/src/main/java/pulse/ui/components/RangeTextFields.java b/src/main/java/pulse/ui/components/RangeTextFields.java index a146e55..dbfcd7c 100644 --- a/src/main/java/pulse/ui/components/RangeTextFields.java +++ b/src/main/java/pulse/ui/components/RangeTextFields.java @@ -3,6 +3,7 @@ import java.awt.Color; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; +import java.io.Serializable; import java.text.DecimalFormat; import java.text.ParseException; import java.util.logging.Level; @@ -19,20 +20,19 @@ import pulse.ui.components.panels.ChartToolbar; /** - * Two JFormattedTextFields used to display the range of the currently - * selected task. + * Two JFormattedTextFields used to display the range of the currently selected + * task. */ -public final class RangeTextFields { +public final class RangeTextFields implements Serializable { private JFormattedTextField lowerLimitField; private JFormattedTextField upperLimitField; /** - * Creates textfield objects, which may be accessed with getters from this instance. - * Additionally, binds listeners to all current and future tasks in order to observe - * and reflect upon the changes with the textfield. + * Creates textfield objects, which may be accessed with getters from this + * instance. Additionally, binds listeners to all current and future tasks + * in order to observe and reflect upon the changes with the textfield. */ - public RangeTextFields() { initTextFields(); @@ -53,18 +53,17 @@ public RangeTextFields() { //when a new task is selected instance.addSelectionListener((TaskSelectionEvent e) -> { var task = instance.getSelectedTask(); - var segment = ( (ExperimentalData) task.getInput() ).getRange().getSegment(); + var segment = ((ExperimentalData) task.getInput()).getRange().getSegment(); //update the textfield values lowerLimitField.setValue(segment.getMinimum()); upperLimitField.setValue(segment.getMaximum()); }); } - + /* Creates a formatter for the textfields - */ - + */ private NumberFormatter initFormatter() { var format = new DecimalFormat(); format.setMinimumFractionDigits(1); @@ -83,18 +82,19 @@ private NumberFormatter initFormatter() { formatter.setOverwriteMode(true); return formatter; } - + /** * Checks if the candidate value produced by the formatter is sensible, i.e. - * if it lies within the bounds defined in the Range class. + * if it lies within the bounds defined in the Range class. + * * @param jtf the textfield containing the candidate value as text - * @param upperBound whether the upper bound is checked ({@code false} if the lower bound is checked) + * @param upperBound whether the upper bound is checked ({@code false} if + * the lower bound is checked) * @return {@code true} if the edit may proceed */ - private static boolean isEditValid(JFormattedTextField jtf, boolean upperBound) { - Range range = ( (ExperimentalData) TaskManager.getManagerInstance().getSelectedTask() - .getInput() ).getRange(); + Range range = ((ExperimentalData) TaskManager.getManagerInstance().getSelectedTask() + .getInput()).getRange(); double candidateValue = 0.0; try { @@ -107,12 +107,11 @@ private static boolean isEditValid(JFormattedTextField jtf, boolean upperBound) return range.boundLimits(upperBound).contains(candidateValue); } - + /** - * Creates a formatter and initialised the textfields, setting up rules - * for edit validation. + * Creates a formatter and initialised the textfields, setting up rules for + * edit validation. */ - private void initTextFields() { var instance = TaskManager.getManagerInstance(); @@ -153,7 +152,7 @@ public void commitEdit() throws ParseException { } }; - + var fl = new FocusListener() { @Override public void focusGained(FocusEvent arg0) { @@ -189,11 +188,11 @@ private void updateTextfieldsFromTask(SearchTask newTask) { } }); } - + public JFormattedTextField getLowerLimitField() { return lowerLimitField; } - + public JFormattedTextField getUpperLimitField() { return upperLimitField; } diff --git a/src/main/java/pulse/ui/components/ResidualsChart.java b/src/main/java/pulse/ui/components/ResidualsChart.java index 7a78ee2..2f31108 100644 --- a/src/main/java/pulse/ui/components/ResidualsChart.java +++ b/src/main/java/pulse/ui/components/ResidualsChart.java @@ -1,9 +1,8 @@ package pulse.ui.components; import static java.util.Objects.requireNonNull; -import static org.jfree.chart.plot.PlotOrientation.VERTICAL; - import org.jfree.chart.ChartFactory; +import static org.jfree.chart.plot.PlotOrientation.VERTICAL; import org.jfree.data.statistics.HistogramDataset; import org.jfree.data.statistics.HistogramType; @@ -14,13 +13,11 @@ public class ResidualsChart extends AuxPlotter { private int binCount; public ResidualsChart(String xLabel, String yLabel) { - super(xLabel, yLabel); - binCount = 32; - } - - @Override - public void createChart(String xLabel, String yLabel) { setChart(ChartFactory.createHistogram("", xLabel, yLabel, null, VERTICAL, true, true, false)); + setPlot(getChart().getXYPlot()); + getChart().removeLegend(); + setFonts(); + binCount = 32; } @Override diff --git a/src/main/java/pulse/ui/components/ResultTable.java b/src/main/java/pulse/ui/components/ResultTable.java index d718926..e2b2b93 100644 --- a/src/main/java/pulse/ui/components/ResultTable.java +++ b/src/main/java/pulse/ui/components/ResultTable.java @@ -44,7 +44,6 @@ public ResultTable(ResultFormat fmt) { var model = new ResultTableModel(fmt); setModel(model); - setRowSorter(sorter()); model.addListener(event -> setRowSorter(sorter())); @@ -60,6 +59,12 @@ public ResultTable(ResultFormat fmt) { headersSize.height = RESULTS_HEADER_HEIGHT; getTableHeader().setPreferredSize(headersSize); + resetSession(); + } + + public void resetSession() { + ((ResultTableModel)getModel()).resetSession(); + /* * Listen to TaskTable and select appropriate results when task selection * changes @@ -76,10 +81,10 @@ public ResultTable(ResultFormat fmt) { * Automatically add finished tasks to this result table Automatically remove * results if corresponding task is removed */ - TaskManager.getManagerInstance().addTaskRepositoryListener((TaskRepositoryEvent e) -> { + instance.addTaskRepositoryListener((TaskRepositoryEvent e) -> { var t = instance.getTask(e.getId()); - - if(t != null) { + + if (t != null) { var cc = (Calculation) t.getResponse(); @@ -112,10 +117,12 @@ public ResultTable(ResultFormat fmt) { default: break; } - + } }); - + + setRowSorter(sorter()); + } public void clear() { @@ -124,7 +131,8 @@ public void clear() { } private TableRowSorter sorter() { - var sorter = new TableRowSorter((ResultTableModel) getModel()); + var model = (ResultTableModel) getModel(); + var sorter = new TableRowSorter(model); var list = new ArrayList(); Comparator numericComparator = (i1, i2) -> i1.compareTo(i2); diff --git a/src/main/java/pulse/ui/components/TaskBox.java b/src/main/java/pulse/ui/components/TaskBox.java index 9be85ed..13b8445 100644 --- a/src/main/java/pulse/ui/components/TaskBox.java +++ b/src/main/java/pulse/ui/components/TaskBox.java @@ -1,6 +1,5 @@ package pulse.ui.components; -import static java.awt.Color.WHITE; import static java.awt.event.ItemEvent.SELECTED; import static pulse.ui.Messages.getString; @@ -46,11 +45,10 @@ public TaskBox() { }); } - public void init() { + public final void init() { setMaximumSize(new Dimension(32767, 24)); setMinimumSize(new Dimension(250, 20)); setToolTipText(getString("TaskBox.DefaultText")); //$NON-NLS-1$ - setBackground(WHITE); } } diff --git a/src/main/java/pulse/ui/components/TaskPopupMenu.java b/src/main/java/pulse/ui/components/TaskPopupMenu.java index 5dc2c26..621210c 100644 --- a/src/main/java/pulse/ui/components/TaskPopupMenu.java +++ b/src/main/java/pulse/ui/components/TaskPopupMenu.java @@ -27,6 +27,8 @@ import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JSeparator; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; import pulse.input.ExperimentalData; import pulse.problem.schemes.solvers.Solver; @@ -61,10 +63,9 @@ public TaskPopupMenu() { ICON_GRAPH); itemExtendedChart.addActionListener(e -> plot(true)); - var instance = TaskManager.getManagerInstance(); - var itemShowMeta = new JMenuItem("Show metadata", ICON_METADATA); itemShowMeta.addActionListener((ActionEvent e) -> { + var instance = TaskManager.getManagerInstance(); var t = instance.getSelectedTask(); if (t == null) { showMessageDialog(getWindowAncestor((Component) e.getSource()), @@ -79,13 +80,8 @@ public TaskPopupMenu() { var itemShowStatus = new JMenuItem("What is missing?", ICON_MISSING); - instance.addSelectionListener(event -> { - instance.getSelectedTask().checkProblems(false); - var details = instance.getSelectedTask().getStatus().getDetails(); - itemShowStatus.setEnabled((details != null) & (details != NONE)); - }); - itemShowStatus.addActionListener((ActionEvent e) -> { + var instance = TaskManager.getManagerInstance(); var t = instance.getSelectedTask(); if (t != null) { var d = t.getStatus().getDetails(); @@ -96,13 +92,14 @@ public TaskPopupMenu() { var itemExecute = new JMenuItem(getString("TaskTablePopupMenu.Execute"), ICON_RUN); //$NON-NLS-1$ itemExecute.addActionListener((ActionEvent e) -> { + var instance = TaskManager.getManagerInstance(); var t = instance.getSelectedTask(); if (t == null) { showMessageDialog(getWindowAncestor((Component) e.getSource()), getString("TaskTablePopupMenu.EmptySelection"), //$NON-NLS-1$ getString("TaskTablePopupMenu.ErrorTitle"), ERROR_MESSAGE); //$NON-NLS-1$ } else { - t.checkProblems(true); + t.checkProblems(); var status = t.getStatus(); if (status == DONE) { @@ -130,11 +127,12 @@ public TaskPopupMenu() { var itemReset = new JMenuItem(getString("TaskTablePopupMenu.Reset"), ICON_RESET); - itemReset.addActionListener((ActionEvent arg0) -> instance.getSelectedTask().clear()); + itemReset.addActionListener((ActionEvent arg0) -> TaskManager.getManagerInstance().getSelectedTask().clear()); var itemGenerateResult = new JMenuItem(getString("TaskTablePopupMenu.GenerateResult"), ICON_RESULT); itemGenerateResult.addActionListener((ActionEvent arg0) -> { + var instance = TaskManager.getManagerInstance(); var t = instance.getSelectedTask(); if (t == null) { return; @@ -152,8 +150,32 @@ public TaskPopupMenu() { itemViewStored.setEnabled(false); - itemViewStored.addActionListener(arg0 -> instance.notifyListeners( - new TaskRepositoryEvent(TASK_BROWSING_REQUEST, instance.getSelectedTask().getIdentifier()))); + itemViewStored.addActionListener(arg0 -> { + var instance = TaskManager.getManagerInstance(); + instance.notifyListeners( + new TaskRepositoryEvent(TASK_BROWSING_REQUEST, instance.getSelectedTask().getIdentifier())); + }); + + this.addPopupMenuListener(new PopupMenuListener() { + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + var instance = TaskManager.getManagerInstance(); + instance.getSelectedTask().checkProblems(); + var details = instance.getSelectedTask().getStatus().getDetails(); + itemShowStatus.setEnabled((details != null) & (details != NONE)); + } + + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + // + } + + @Override + public void popupMenuCanceled(PopupMenuEvent e) { + //. + } + + }); add(itemShowMeta); add(itemShowStatus); diff --git a/src/main/java/pulse/ui/components/TaskTable.java b/src/main/java/pulse/ui/components/TaskTable.java index c7cff7b..e69ba89 100644 --- a/src/main/java/pulse/ui/components/TaskTable.java +++ b/src/main/java/pulse/ui/components/TaskTable.java @@ -56,32 +56,11 @@ public TaskTable() { setSelectionMode(SINGLE_INTERVAL_SELECTION); setShowHorizontalLines(false); - var model = new TaskTableModel(); - setModel(model); - var th = new TableHeader(getColumnModel()); setTableHeader(th); - getTableHeader().setPreferredSize(new Dimension(50, HEADER_HEIGHT)); - setAutoCreateRowSorter(true); - var sorter = new TableRowSorter(); - sorter.setModel(model); - var list = new ArrayList(); - - for (var i = 0; i < this.getModel().getColumnCount(); i++) { - list.add(new RowSorter.SortKey(i, ASCENDING)); - if (i == TaskTableModel.STATUS_COLUMN) { - sorter.setComparator(i, statusComparator); - } else { - sorter.setComparator(i, numericComparator); - } - } - - sorter.setSortKeys(list); - setRowSorter(sorter); - initListeners(); menu = new TaskPopupMenu(); @@ -89,8 +68,6 @@ public TaskTable() { public void initListeners() { - var instance = TaskManager.getManagerInstance(); - /* * mouse listener */ @@ -99,6 +76,8 @@ public void initListeners() { @Override public void mouseClicked(MouseEvent e) { + var instance = TaskManager.getManagerInstance(); + if (rowAtPoint(e.getPoint()) >= 0 && rowAtPoint(e.getPoint()) == getSelectedRow() && isRightMouseButton(e)) { var task = instance.getSelectedTask(); menu.getItemViewStored().setEnabled(task.getStoredCalculations().size() > 0); @@ -116,21 +95,50 @@ public void mouseClicked(MouseEvent e) { var reference = this; lsm.addListSelectionListener((ListSelectionEvent e) -> { + var instance = TaskManager.getManagerInstance(); if (!lsm.getValueIsAdjusting() && !lsm.isSelectionEmpty()) { var id = (Identifier) getValueAt(lsm.getMinSelectionIndex(), 0); instance.selectTask(id, reference); } }); + resetSession(); + + } + + public void resetSession() { + var model = new TaskTableModel(); + setModel(model); + + var sorter = new TableRowSorter(); + sorter.setModel(model); + var list = new ArrayList(); + + for (var i = 0; i < this.getModel().getColumnCount(); i++) { + list.add(new RowSorter.SortKey(i, ASCENDING)); + if (i == TaskTableModel.STATUS_COLUMN) { + sorter.setComparator(i, statusComparator); + } else { + sorter.setComparator(i, numericComparator); + } + } + + sorter.setSortKeys(list); + setRowSorter(sorter); + + var instance = TaskManager.getManagerInstance(); instance.addSelectionListener((TaskSelectionEvent e) -> { + // simply ignore call if event is triggered by taskTable - if (e.getSource() instanceof TaskTable) { + if (e.getSource() == this) { return; } var id = instance.getSelectedTask().getIdentifier(); Identifier idFromTable = null; int i = 0; + + clearSelection(); for (i = 0; i < getRowCount() && !id.equals(idFromTable); i++) { idFromTable = (Identifier) getValueAt(i, 0); @@ -139,9 +147,7 @@ public void mouseClicked(MouseEvent e) { if (i < getRowCount()) { setRowSelectionInterval(i, i); } - clearSelection(); }); - } @Override diff --git a/src/main/java/pulse/ui/components/TextLogPane.java b/src/main/java/pulse/ui/components/TextLogPane.java new file mode 100644 index 0000000..de79129 --- /dev/null +++ b/src/main/java/pulse/ui/components/TextLogPane.java @@ -0,0 +1,84 @@ +package pulse.ui.components; + +import pulse.tasks.logs.AbstractLogger; +import static java.lang.System.err; +import static javax.swing.text.DefaultCaret.ALWAYS_UPDATE; +import static pulse.ui.Messages.getString; + +import java.io.IOException; +import javax.swing.JComponent; + +import javax.swing.JEditorPane; +import javax.swing.JScrollPane; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultCaret; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLEditorKit; + +import pulse.tasks.logs.Log; +import pulse.tasks.logs.LogEntry; + +@SuppressWarnings("serial") +public class TextLogPane extends AbstractLogger { + + private final JEditorPane editor; + private final JScrollPane pane; + + public TextLogPane() { + editor = new JEditorPane(); + editor.setContentType("text/html"); + editor.setEditable(false); + ((DefaultCaret) editor.getCaret()).setUpdatePolicy(ALWAYS_UPDATE); + pane = new JScrollPane(); + pane.setViewportView(editor); + } + + @Override + public void post(LogEntry logEntry) { + post(logEntry.toString()); + } + + @Override + public void post(String text) { + + final var doc = (HTMLDocument) editor.getDocument(); + final var kit = (HTMLEditorKit) editor.getEditorKit(); + try { + kit.insertHTML(doc, doc.getLength(), text, 0, 0, null); + } catch (BadLocationException e) { + err.println(getString("LogPane.InsertError")); //$NON-NLS-1$ + } catch (IOException e) { + err.println(getString("LogPane.PrintError")); //$NON-NLS-1$ + } + + } + + public void printTimeTaken(Log log) { + var time = log.timeTaken(); + var sb = new StringBuilder(); + sb.append(getString("LogPane.TimeTaken")); //$NON-NLS-1$ + sb.append(time[0]).append(getString("LogPane.Seconds")); //$NON-NLS-1$ + sb.append(time[1]).append(getString("LogPane.Milliseconds")); //$NON-NLS-1$ + post(sb.toString()); + } + + @Override + public void clear() { + try { + editor.getDocument().remove(0, editor.getDocument().getLength()); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + + @Override + public JComponent getGUIComponent() { + return pane; + } + + @Override + public boolean isEmpty() { + return editor.getDocument().getLength() < 1; + } + +} diff --git a/src/main/java/pulse/ui/components/buttons/ExecutionButton.java b/src/main/java/pulse/ui/components/buttons/ExecutionButton.java index 30ea042..8ef13f4 100644 --- a/src/main/java/pulse/ui/components/buttons/ExecutionButton.java +++ b/src/main/java/pulse/ui/components/buttons/ExecutionButton.java @@ -28,9 +28,9 @@ public ExecutionButton() { setIcon(state.getIcon()); setToolTipText(state.getMessage()); - var instance = TaskManager.getManagerInstance(); - this.addActionListener((ActionEvent e) -> { + var instance = TaskManager.getManagerInstance(); + /* * STOP PRESSED? */ @@ -49,7 +49,7 @@ public ExecutionButton() { return; } var problematicTask = instance.getTaskList().stream().filter(t -> { - t.checkProblems(true); + t.checkProblems(); return t.getStatus() == INCOMPLETE; }).findFirst(); if (problematicTask.isPresent()) { @@ -62,6 +62,12 @@ public ExecutionButton() { } }); + resetSession(); + + } + + public void resetSession() { + var instance = TaskManager.getManagerInstance(); instance.addTaskRepositoryListener((TaskRepositoryEvent e) -> { switch (e.getState()) { case TASK_SUBMITTED: @@ -81,7 +87,6 @@ public ExecutionButton() { return; } }); - } public void setExecutionState(ExecutionState state) { diff --git a/src/main/java/pulse/ui/components/buttons/LoaderButton.java b/src/main/java/pulse/ui/components/buttons/LoaderButton.java index 46c9b43..09733d9 100644 --- a/src/main/java/pulse/ui/components/buttons/LoaderButton.java +++ b/src/main/java/pulse/ui/components/buttons/LoaderButton.java @@ -6,12 +6,8 @@ import static javax.swing.JOptionPane.INFORMATION_MESSAGE; import static javax.swing.JOptionPane.showMessageDialog; import static javax.swing.SwingUtilities.getWindowAncestor; -import static pulse.input.InterpolationDataset.getDataset; -import static pulse.input.InterpolationDataset.StandartType.DENSITY; -import static pulse.input.InterpolationDataset.StandartType.HEAT_CAPACITY; import static pulse.io.readers.ReaderManager.getDatasetExtensions; import static pulse.ui.Messages.getString; -import static pulse.ui.components.DataLoader.load; import java.awt.Color; import java.awt.Component; @@ -19,7 +15,6 @@ import java.io.File; import java.io.IOException; -import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.UIManager; @@ -27,35 +22,34 @@ import org.apache.commons.math3.exception.OutOfRangeException; import pulse.input.InterpolationDataset; -import pulse.ui.Messages; +import pulse.tasks.TaskManager; +import static pulse.ui.components.DataLoader.loadDensity; +import static pulse.ui.components.DataLoader.loadSpecificHeat; import pulse.util.ImageUtils; @SuppressWarnings("serial") public class LoaderButton extends JButton { - private InterpolationDataset.StandartType dataType; + private final DataType dataType; private static File dir; private final static Color NOT_HIGHLIGHTED = UIManager.getColor("Button.background"); - private final static Color HIGHLIGHTED = ImageUtils.blend(NOT_HIGHLIGHTED, Color.red, 0.75f); + private final static Color HIGHLIGHTED = ImageUtils.blend(NOT_HIGHLIGHTED, Color.red, 0.35f); - public LoaderButton() { + public LoaderButton(DataType type) { super(); + this.dataType = type; init(); } - public LoaderButton(String str) { + public LoaderButton(DataType type, String str) { super(str); + this.dataType = type; init(); } - - public void init() { - - InterpolationDataset.addListener(e -> { - if (dataType == e) { - highlight(false); - } - }); + + public final void init() { + highlight(false); addActionListener((ActionEvent arg0) -> { var fileChooser = new JFileChooser(); @@ -73,10 +67,10 @@ public void init() { try { switch (dataType) { case HEAT_CAPACITY: - load(HEAT_CAPACITY, fileChooser.getSelectedFile()); + loadSpecificHeat(fileChooser.getSelectedFile()); break; case DENSITY: - load(DENSITY, fileChooser.getSelectedFile()); + loadDensity(fileChooser.getSelectedFile()); break; default: throw new IllegalStateException("Unrecognised type: " + dataType); @@ -86,20 +80,19 @@ public void init() { showMessageDialog(getWindowAncestor((Component) arg0.getSource()), getString("LoaderButton.ReadError"), //$NON-NLS-1$ getString("LoaderButton.IOError"), //$NON-NLS-1$ ERROR_MESSAGE); - } - catch(OutOfRangeException ofre) { + } catch (OutOfRangeException ofre) { getDefaultToolkit().beep(); StringBuilder sb = new StringBuilder(getString("TextWrap.0")); - sb.append(getString("LoaderButton.OFRErrorDescriptor") ); + sb.append(getString("LoaderButton.OFRErrorDescriptor")); sb.append(ofre.getMessage()); sb.append(getString("LoaderButton.OFRErrorDescriptor2")); sb.append(getString("TextWrap.1")); - showMessageDialog(getWindowAncestor((Component) arg0.getSource()), - sb.toString(), + showMessageDialog(getWindowAncestor((Component) arg0.getSource()), + sb.toString(), getString("LoaderButton.OFRError"), //$NON-NLS-1$ - ERROR_MESSAGE); + ERROR_MESSAGE); } - var size = getDataset(dataType).getData().size(); + int size = getDataset().getData().size(); var label = ""; switch (dataType) { case HEAT_CAPACITY: @@ -113,22 +106,31 @@ public void init() { default: throw new IllegalStateException("Unknown data type: " + dataType); } + StringBuilder sb = new StringBuilder(""); + sb.append(label).append(" data loaded! A total of ").append(size).append(" data points loaded."); showMessageDialog(getWindowAncestor((Component) arg0.getSource()), - "" + label + " data loaded! A total of " + size + " data points loaded.", + sb.toString(), "Data loaded", INFORMATION_MESSAGE); + highlight(false); }); } - - public void setDataType(InterpolationDataset.StandartType dataType) { - this.dataType = dataType; + + public InterpolationDataset getDataset() { + var i = TaskManager.getManagerInstance(); + return dataType == DataType.HEAT_CAPACITY ? + i.getSpecificHeatDataset() : i.getDensityDataset(); } - + public void highlight(boolean highlighted) { - setBorder(highlighted ? BorderFactory.createLineBorder(HIGHLIGHTED) : null); + setBackground(highlighted ? HIGHLIGHTED : NOT_HIGHLIGHTED); } public void highlightIfNeeded() { - highlight(getDataset(dataType) == null); + highlight(getDataset() == null); + } + + public enum DataType { + HEAT_CAPACITY, DENSITY; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java b/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java index b297d1a..4c363b5 100644 --- a/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/AccessibleTableRenderer.java @@ -1,14 +1,11 @@ package pulse.ui.components.controllers; -import static java.awt.Color.RED; - import java.awt.Component; import java.awt.Font; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JTable; -import javax.swing.UIManager; import pulse.properties.Flag; import pulse.properties.NumericProperty; @@ -28,33 +25,24 @@ public AccessibleTableRenderer() { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Component renderer = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + Component result = null; if (value instanceof Flag) { - renderer = new IconCheckBox((boolean) ((Property) value).getValue()); - ((IconCheckBox) renderer).setHorizontalAlignment(CENTER); + result = new IconCheckBox((boolean) ((Property) value).getValue()); + ((IconCheckBox) result).setHorizontalAlignment(CENTER); } else if (value instanceof PropertyHolder) { - renderer = initButton(value.toString()); - } - else if (value instanceof NumericProperty) { - //default - } - else if (value instanceof Property) { - var label = (JLabel) super.getTableCellRendererComponent(table, - ((Property) value).getDescriptor(true), isSelected, - hasFocus, row, column); - label.setHorizontalAlignment(JLabel.CENTER); - label.setFont(label.getFont().deriveFont(Font.BOLD)); - return label; + var sb = new StringBuilder("Click to Edit/View "); + sb.append(((PropertyHolder) value).getSimpleName()); + sb.append("..."); + result = new JButton(sb.toString()); + ((JButton) result).setToolTipText(value.toString()); + ((JButton) result).setHorizontalAlignment(LEFT); + } else { + result = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } - return renderer; - } + return result; - private JButton initButton(String str) { - var button = new JButton(str); - button.setToolTipText(str); - return button; } } diff --git a/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java b/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java index 526823f..9f01e29 100644 --- a/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java +++ b/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java @@ -1,13 +1,11 @@ package pulse.ui.components.controllers; -import com.alee.utils.swing.PopupMenuAdapter; import java.awt.Component; import java.awt.event.ItemEvent; import javax.swing.DefaultCellEditor; import javax.swing.JComboBox; import javax.swing.JTable; -import javax.swing.event.PopupMenuEvent; import pulse.util.InstanceDescriptor; @@ -31,7 +29,7 @@ public Component getTableCellEditorComponent(JTable table, Object value, boolean if (e.getStateChange() == ItemEvent.SELECTED) { try { descriptor.attemptUpdate(e.getItem()); - } catch(NullPointerException npe) { + } catch (NullPointerException npe) { String text = "Error updating " + descriptor.getDescriptor(false) + ". Cannot be set to " + e.getItem(); System.out.println(text); @@ -40,16 +38,6 @@ public Component getTableCellEditorComponent(JTable table, Object value, boolean } }); - combobox.addPopupMenuListener(new PopupMenuAdapter() { - - @Override - public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { - fireEditingCanceled(); - } - - } - ); - return combobox; } diff --git a/src/main/java/pulse/ui/components/controllers/NumberEditor.java b/src/main/java/pulse/ui/components/controllers/NumberEditor.java index c51120f..3aa4149 100644 --- a/src/main/java/pulse/ui/components/controllers/NumberEditor.java +++ b/src/main/java/pulse/ui/components/controllers/NumberEditor.java @@ -61,10 +61,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/ public class NumberEditor extends DefaultCellEditor { - /** - * - */ - private static final long serialVersionUID = 1L; JFormattedTextField ftf; NumberFormat numberFormat; private boolean DEBUG = false; @@ -104,10 +100,6 @@ public NumberEditor(NumericProperty property) { // JFormattedTextField's focusLostBehavior property.) ftf.getInputMap().put(getKeyStroke(VK_ENTER, 0), "check"); ftf.getActionMap().put("check", new AbstractAction() { - /** - * - */ - private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { diff --git a/src/main/java/pulse/ui/components/controllers/NumericPropertyRenderer.java b/src/main/java/pulse/ui/components/controllers/NumericPropertyRenderer.java index e655f9b..2d07657 100644 --- a/src/main/java/pulse/ui/components/controllers/NumericPropertyRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/NumericPropertyRenderer.java @@ -7,10 +7,8 @@ import javax.swing.JTable; import javax.swing.UIManager; import javax.swing.table.DefaultTableCellRenderer; -import pulse.math.Segment; import pulse.properties.NumericProperty; -import pulse.properties.Property; import pulse.properties.NumericPropertyFormatter; @SuppressWarnings("serial") @@ -29,25 +27,21 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole if (value instanceof NumericProperty) { var jtf = initTextField((NumericProperty) value, table.isRowSelected(row)); - if (table.getEditorComponent() != null) { result = jtf; } else { - result = new JLabel(jtf.getText(), JLabel.RIGHT); - jtf = null; + result = (JLabel) super.getTableCellRendererComponent(table, + jtf.getText(), isSelected, hasFocus, row, column); + ((JLabel) result).setHorizontalAlignment(RIGHT); } - } else { var superRenderer = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); superRenderer.setHorizontalAlignment(JLabel.LEFT); - superRenderer.setBackground( - isSelected - ? UIManager.getColor("JFormattedTextField.selectionBackground") - : UIManager.getColor("JFormattedTextField.background")); result = superRenderer; } + result.setForeground(UIManager.getColor("List.foreground")); return result; } @@ -59,4 +53,4 @@ private static JFormattedTextField initTextField(NumericProperty np, boolean row return jtf; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/ui/components/controllers/ProblemCellRenderer.java b/src/main/java/pulse/ui/components/controllers/ProblemCellRenderer.java index 7c36077..dd621b9 100644 --- a/src/main/java/pulse/ui/components/controllers/ProblemCellRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/ProblemCellRenderer.java @@ -4,19 +4,18 @@ import javax.swing.ImageIcon; import javax.swing.JTree; -import javax.swing.UIManager; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; -import com.alee.managers.icon.LazyIcon; - +//import com.alee.managers.icon.LazyIcon; import pulse.problem.statements.Problem; import pulse.util.ImageUtils; +import static pulse.util.ImageUtils.loadIcon; @SuppressWarnings("serial") public class ProblemCellRenderer extends DefaultTreeCellRenderer { - private static ImageIcon defaultIcon = (ImageIcon) ((LazyIcon) UIManager.getIcon("Tree.leafIcon")).getIcon(); + private static ImageIcon defaultIcon = loadIcon("leaf.png", 16); public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { diff --git a/src/main/java/pulse/ui/components/controllers/SearchListRenderer.java b/src/main/java/pulse/ui/components/controllers/SearchListRenderer.java index e74951a..402bc41 100644 --- a/src/main/java/pulse/ui/components/controllers/SearchListRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/SearchListRenderer.java @@ -2,7 +2,6 @@ import static javax.swing.BorderFactory.createEmptyBorder; -import java.awt.Color; import java.awt.Component; import javax.swing.DefaultListCellRenderer; @@ -18,7 +17,6 @@ public Component getListCellRendererComponent(JList list, Object value, int i var renderer = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); ((JComponent) renderer).setBorder(createEmptyBorder(10, 10, 10, 10)); - renderer.setForeground(isSelected ? Color.DARK_GRAY : Color.white); return renderer; diff --git a/src/main/java/pulse/ui/components/controllers/TaskTableRenderer.java b/src/main/java/pulse/ui/components/controllers/TaskTableRenderer.java index 85d3ba6..8bc8369 100644 --- a/src/main/java/pulse/ui/components/controllers/TaskTableRenderer.java +++ b/src/main/java/pulse/ui/components/controllers/TaskTableRenderer.java @@ -3,16 +3,13 @@ import static java.awt.Font.BOLD; import java.awt.Component; -import java.awt.Font; import javax.swing.JLabel; import javax.swing.JTable; import pulse.properties.NumericProperty; -import pulse.properties.Property; import pulse.tasks.Identifier; import pulse.tasks.logs.Status; -import pulse.util.PropertyHolder; @SuppressWarnings("serial") public class TaskTableRenderer extends NumericPropertyRenderer { @@ -37,11 +34,11 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole superRenderer.setForeground(((Status) value).getColor()); superRenderer.setFont(superRenderer.getFont().deriveFont(BOLD)); - ((JLabel)superRenderer).setHorizontalAlignment(JLabel.CENTER); + ((JLabel) superRenderer).setHorizontalAlignment(JLabel.CENTER); return superRenderer; - } + } return superRenderer; diff --git a/src/main/java/pulse/ui/components/listeners/FrameVisibilityRequestListener.java b/src/main/java/pulse/ui/components/listeners/FrameVisibilityRequestListener.java index fe79169..121f70b 100644 --- a/src/main/java/pulse/ui/components/listeners/FrameVisibilityRequestListener.java +++ b/src/main/java/pulse/ui/components/listeners/FrameVisibilityRequestListener.java @@ -3,7 +3,6 @@ public interface FrameVisibilityRequestListener { public void onProblemStatementShowRequest(); - public void onSearchSettingsShowRequest(); -} +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/listeners/LogExportListener.java b/src/main/java/pulse/ui/components/listeners/LogExportListener.java deleted file mode 100644 index 99a32ae..0000000 --- a/src/main/java/pulse/ui/components/listeners/LogExportListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package pulse.ui.components.listeners; - -public interface LogExportListener { - - public void onLogExportRequest(); - -} diff --git a/src/main/java/pulse/ui/components/listeners/LogListener.java b/src/main/java/pulse/ui/components/listeners/LogListener.java new file mode 100644 index 0000000..a498817 --- /dev/null +++ b/src/main/java/pulse/ui/components/listeners/LogListener.java @@ -0,0 +1,9 @@ +package pulse.ui.components.listeners; + +public interface LogListener { + + public void onLogExportRequest(); + + public void onLogModeChanged(boolean graphical); + +} diff --git a/src/main/java/pulse/ui/components/listeners/MouseOnMarkerListener.java b/src/main/java/pulse/ui/components/listeners/MouseOnMarkerListener.java index 834dc1c..b619caf 100644 --- a/src/main/java/pulse/ui/components/listeners/MouseOnMarkerListener.java +++ b/src/main/java/pulse/ui/components/listeners/MouseOnMarkerListener.java @@ -29,12 +29,12 @@ public class MouseOnMarkerListener implements ChartMouseListener { private final MovableValueMarker lower; private final MovableValueMarker upper; - + private final Chart chart; private final double margin; - + private final static Cursor CROSSHAIR = new Cursor(Cursor.CROSSHAIR_CURSOR); - private final static Cursor RESIZE = new Cursor(Cursor.E_RESIZE_CURSOR); + private final static Cursor RESIZE = new Cursor(Cursor.E_RESIZE_CURSOR); public MouseOnMarkerListener(Chart chart, MovableValueMarker lower, MovableValueMarker upper, double margin) { this.chart = chart; @@ -51,33 +51,31 @@ public void chartMouseClicked(ChartMouseEvent arg0) { @Override public void chartMouseMoved(ChartMouseEvent arg0) { double xCoord = chart.xCoord(arg0.getTrigger()); - highlightMarker(xCoord); + highlightMarker(xCoord); } private void highlightMarker(double xCoord) { if (xCoord > (lower.getValue() - margin) & xCoord < (lower.getValue() + margin)) { - - lower.setState(MovableValueMarker.State.SELECTED); + + lower.setState(MovableValueMarker.State.SELECTED); chart.getChartPanel().setCursor(RESIZE); - - } - else if (xCoord > (upper.getValue() - margin) + + } else if (xCoord > (upper.getValue() - margin) & xCoord < (upper.getValue() + margin)) { - + upper.setState(MovableValueMarker.State.SELECTED); chart.getChartPanel().setCursor(RESIZE); - - } - else { - + + } else { + lower.setState(MovableValueMarker.State.IDLE); upper.setState(MovableValueMarker.State.IDLE); chart.getChartPanel().setCursor(CROSSHAIR); - + } } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/ui/components/listeners/ResultListener.java b/src/main/java/pulse/ui/components/listeners/ResultListener.java index 17ba3bf..4ba96d2 100644 --- a/src/main/java/pulse/ui/components/listeners/ResultListener.java +++ b/src/main/java/pulse/ui/components/listeners/ResultListener.java @@ -6,4 +6,4 @@ public interface ResultListener { public void onFormatChanged(ResultFormatEvent fme); -} +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/components/models/ParameterTableModel.java b/src/main/java/pulse/ui/components/models/ParameterTableModel.java index 66aa0cb..ec1bf9c 100644 --- a/src/main/java/pulse/ui/components/models/ParameterTableModel.java +++ b/src/main/java/pulse/ui/components/models/ParameterTableModel.java @@ -9,19 +9,15 @@ import javax.swing.table.AbstractTableModel; -import pulse.input.InterpolationDataset; import pulse.properties.Flag; import pulse.properties.NumericProperties; import pulse.properties.NumericPropertyKeyword; import pulse.search.direction.ActiveFlags; +import pulse.tasks.TaskManager; import pulse.ui.Messages; public class ParameterTableModel extends AbstractTableModel { - - /** - * - */ - private static final long serialVersionUID = 1L; + protected List elements; private final boolean extendedList; @@ -39,7 +35,7 @@ public final void populateWithAllProperties() { elements.add(OPTIMISER_STATISTIC); elements.add(TEST_STATISTIC); elements.add(IDENTIFIER); - elements.addAll(InterpolationDataset.derivableProperties()); + elements.addAll(TaskManager.getManagerInstance().derivableProperties()); } } @@ -55,19 +51,19 @@ public int getColumnCount() { @Override public Object getValueAt(int i, int i1) { - if(i > -1 && i < getRowCount() && i1 > -1 && i1 < getColumnCount()) { + if (i > -1 && i < getRowCount() && i1 > -1 && i1 < getColumnCount()) { var p = NumericProperties.def(elements.get(i)); - return i1 == 0 ? p.getAbbreviation(true) : Messages.getString("TextWrap.2") + - p.getDescriptor(false) + Messages.getString("TextWrap.1"); - } - else + return i1 == 0 ? p.getAbbreviation(true) : Messages.getString("TextWrap.2") + + p.getDescriptor(false) + Messages.getString("TextWrap.1"); + } else { return null; + } } - + public boolean contains(NumericPropertyKeyword key) { return elements.contains(key); } - + public NumericPropertyKeyword getElementAt(int index) { return elements.get(index); } diff --git a/src/main/java/pulse/ui/components/models/ResultTableModel.java b/src/main/java/pulse/ui/components/models/ResultTableModel.java index 8592285..c131f9c 100644 --- a/src/main/java/pulse/ui/components/models/ResultTableModel.java +++ b/src/main/java/pulse/ui/components/models/ResultTableModel.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import static javax.swing.SwingUtilities.invokeLater; import javax.swing.table.DefaultTableModel; @@ -17,6 +18,7 @@ import pulse.tasks.Identifier; import pulse.tasks.SearchTask; +import pulse.tasks.TaskManager; import pulse.tasks.listeners.ResultFormatEvent; import pulse.tasks.logs.Details; import pulse.tasks.logs.Status; @@ -36,8 +38,8 @@ public class ResultTableModel extends DefaultTableModel { public ResultTableModel(ResultFormat fmt, int rowCount) { super(fmt.abbreviations().toArray(), rowCount); - this.fmt = fmt; results = new ArrayList<>(); + this.fmt = fmt; tooltips = tooltips(); listeners = new ArrayList<>(); } @@ -46,6 +48,14 @@ public ResultTableModel(ResultFormat fmt) { this(fmt, 0); } + public void resetSession() { + clear(); + changeFormat(ResultFormat.getInstance()); + var repo = TaskManager.getManagerInstance(); + repo.getTaskList().stream() + .forEach(t -> addRow(t.findBestCalculation().getResult())); + } + public void addListener(ResultListener listener) { listeners.add(listener); } @@ -219,24 +229,28 @@ private List tooltips() { public void addRow(AbstractResult result) { Objects.requireNonNull(result, "Entry added to the results table must not be null"); + var instance = TaskManager.getManagerInstance(); + //ignore average results if (result instanceof Result) { //result must have a valid ancestor! - var ancestor = Objects.requireNonNull(result.specificAncestor(SearchTask.class), - "Result " + result.toString() + " does not belong a SearchTask!"); - - //the ancestor then has the SearchTask type - SearchTask parentTask = (SearchTask) ancestor; + var id = ((Result) result).getTaskIdentifier(); + SearchTask parentTask = instance.getTask(id); //any old result asssociated withis this task - var oldResult = results.stream().filter(r - -> r.specificAncestor(SearchTask.class) == parentTask).findAny(); - - //check the following only if the old result is present - if (oldResult.isPresent()) { - - AbstractResult oldResultExisting = oldResult.get(); + var linkedResults = results.stream() + .filter(r -> r instanceof Result) + .filter(rr -> ((Result) rr).getTaskIdentifier().equals(parentTask.getIdentifier())) + .collect(Collectors.toList()); + + if (linkedResults.size() > 1) { + //table can't contain more than one result associated with a task + throw new IllegalStateException("More than one result found associated with " + parentTask.getIdentifier()); + } else if (linkedResults.size() == 1) { + //check the following only if the old result is present + AbstractResult oldResultExisting = linkedResults.get(0); + //find specific calculation for this result Optional oldCalculation = parentTask.getStoredCalculations().stream() .filter(c -> c.getResult().equals(oldResultExisting)).findAny(); diff --git a/src/main/java/pulse/ui/components/models/SelectedKeysModel.java b/src/main/java/pulse/ui/components/models/SelectedKeysModel.java index 98491bf..f9098f2 100644 --- a/src/main/java/pulse/ui/components/models/SelectedKeysModel.java +++ b/src/main/java/pulse/ui/components/models/SelectedKeysModel.java @@ -1,6 +1,5 @@ package pulse.ui.components.models; - import java.util.ArrayList; import java.util.List; @@ -12,10 +11,6 @@ public class SelectedKeysModel extends DefaultTableModel { - /** - * - */ - private static final long serialVersionUID = 1L; private final List elements; private final List referenceList; private final NumericPropertyKeyword[] mandatorySelection; @@ -36,7 +31,7 @@ public void update(List keys) { elements.clear(); elements.addAll(keys); } - + @Override public int getRowCount() { return elements != null ? elements.size() : 0; @@ -46,29 +41,29 @@ public int getRowCount() { public int getColumnCount() { return 2; } - + @Override public Object getValueAt(int i, int i1) { - if(i > -1 && i < getRowCount() && i1 > -1 && i1 < getColumnCount()) { + if (i > -1 && i < getRowCount() && i1 > -1 && i1 < getColumnCount()) { var p = NumericProperties.def(elements.get(i)); - return i1 == 0 ? p.getAbbreviation(true) : Messages.getString("TextWrap.2") + - p.getDescriptor(false) + Messages.getString("TextWrap.1"); - } - else + return i1 == 0 ? p.getAbbreviation(true) : Messages.getString("TextWrap.2") + + p.getDescriptor(false) + Messages.getString("TextWrap.1"); + } else { return null; + } } - + public void addElement(NumericPropertyKeyword key) { elements.add(key); var e = NumericProperties.def(key); int index = elements.size() - 1; super.fireTableRowsInserted(index, index); } - + public boolean contains(NumericPropertyKeyword key) { return elements.contains(key); } - + public List getData() { return elements; } @@ -76,7 +71,7 @@ public List getData() { public NumericPropertyKeyword getElementAt(int index) { return elements.get(index); } - + public boolean removeElement(NumericPropertyKeyword key) { if (!elements.contains(key)) { @@ -88,11 +83,11 @@ public boolean removeElement(NumericPropertyKeyword key) { return false; } } - + var index = elements.indexOf(key); super.fireTableRowsDeleted(index, index); elements.remove(key); return true; } -} \ No newline at end of file +} diff --git a/src/main/java/pulse/ui/components/models/TaskBoxModel.java b/src/main/java/pulse/ui/components/models/TaskBoxModel.java index 5230f28..317d367 100644 --- a/src/main/java/pulse/ui/components/models/TaskBoxModel.java +++ b/src/main/java/pulse/ui/components/models/TaskBoxModel.java @@ -17,10 +17,6 @@ */ public class TaskBoxModel extends AbstractListModel implements ComboBoxModel { - /** - * - */ - private static final long serialVersionUID = 5394433933807306979L; protected SearchTask selectedTask; public TaskBoxModel() { diff --git a/src/main/java/pulse/ui/components/models/TaskTableModel.java b/src/main/java/pulse/ui/components/models/TaskTableModel.java index 86eef58..2145d57 100644 --- a/src/main/java/pulse/ui/components/models/TaskTableModel.java +++ b/src/main/java/pulse/ui/components/models/TaskTableModel.java @@ -31,14 +31,21 @@ public class TaskTableModel extends DefaultTableModel { public TaskTableModel() { super(new Object[][]{}, - new String[]{def(IDENTIFIER).getAbbreviation(true), + new String[]{def(IDENTIFIER).getAbbreviation(true), def(TEST_TEMPERATURE).getAbbreviation(true), - def(OPTIMISER_STATISTIC).getAbbreviation(true), + def(OPTIMISER_STATISTIC).getAbbreviation(true), def(TEST_STATISTIC).getAbbreviation(true), getString("TaskTable.Status")}); + resetSession(); + } + + public void resetSession() { + //clear all rows + this.setRowCount(0); var instance = TaskManager.getManagerInstance(); - + instance.getTaskList().stream().forEach(t -> addTask(t)); + /* * task removed/added listener */ @@ -49,14 +56,13 @@ public TaskTableModel() { addTask(instance.getTask(e.getId())); } }); - } public void addTask(SearchTask t) { - var temperature = ( (ExperimentalData) t.getInput() ) + var temperature = ((ExperimentalData) t.getInput()) .getMetadata().numericProperty(TEST_TEMPERATURE); var calc = (Calculation) t.getResponse(); - var data = new Object[]{t.getIdentifier(), temperature, + var data = new Object[]{t.getIdentifier(), temperature, calc.getOptimiserStatistic().getStatistic(), t.getNormalityTest().getStatistic(), t.getStatus()}; @@ -65,7 +71,7 @@ public void addTask(SearchTask t) { t.addStatusChangeListener((StateEntry e) -> { setValueAt(e.getState(), searchRow(t.getIdentifier()), STATUS_COLUMN); if (t.getNormalityTest() != null) { - setValueAt(t.getNormalityTest().getStatistic(), + setValueAt(t.getNormalityTest().getStatistic(), searchRow(t.getIdentifier()), TEST_STATISTIC_COLUMN); } }); diff --git a/src/main/java/pulse/ui/components/panels/ChartToolbar.java b/src/main/java/pulse/ui/components/panels/ChartToolbar.java index b4d5e54..bb54a90 100644 --- a/src/main/java/pulse/ui/components/panels/ChartToolbar.java +++ b/src/main/java/pulse/ui/components/panels/ChartToolbar.java @@ -38,7 +38,7 @@ public final class ChartToolbar extends JToolBar { private final static int ICON_SIZE = 16; - private List listeners; + private final List listeners; private RangeTextFields rtf; @@ -171,7 +171,7 @@ private void validateRange(double a, double b) { // set range for all available experimental datasets TaskManager.getManagerInstance().getTaskList() .stream().forEach((aTask) - -> setRange( (ExperimentalData) aTask.getInput(), a, b) + -> setRange((ExperimentalData) aTask.getInput(), a, b) ); } diff --git a/src/main/java/pulse/ui/components/panels/DoubleTablePanel.java b/src/main/java/pulse/ui/components/panels/DoubleTablePanel.java index 11a5c19..d685da9 100644 --- a/src/main/java/pulse/ui/components/panels/DoubleTablePanel.java +++ b/src/main/java/pulse/ui/components/panels/DoubleTablePanel.java @@ -1,18 +1,3 @@ -/* - * Copyright 2021 kotik. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package pulse.ui.components.panels; import java.awt.GridBagConstraints; @@ -35,13 +20,13 @@ public DoubleTablePanel(JTable leftTable, String titleLeft, JTable rightTable, S super(); initComponents(leftTable, titleLeft, rightTable, titleRight); - + moveRightBtn.addActionListener(e -> { var model = (SelectedKeysModel) rightTable.getModel(); - NumericPropertyKeyword key = ( (ParameterTableModel) leftTable.getModel() ) - .getElementAt(leftTable - .convertRowIndexToModel(leftTable.getSelectedRow())); + NumericPropertyKeyword key = ((ParameterTableModel) leftTable.getModel()) + .getElementAt(leftTable + .convertRowIndexToModel(leftTable.getSelectedRow())); if (key != null) { if (!model.contains(key)) { @@ -72,7 +57,7 @@ public DoubleTablePanel(JTable leftTable, String titleLeft, JTable rightTable, S } }); - + } public void initComponents(JTable leftTable, String titleLeft, JTable rightTable, String titleRight) { @@ -87,8 +72,7 @@ public void initComponents(JTable leftTable, String titleLeft, JTable rightTable var borderLeft = createTitledBorder(titleLeft); leftScroller.setBorder(borderLeft); - borderLeft.setTitleColor(java.awt.Color.WHITE); - + leftTable.setRowHeight(80); leftScroller.setViewportView(leftTable); @@ -102,7 +86,6 @@ public void initComponents(JTable leftTable, String titleLeft, JTable rightTable var borderRight = createTitledBorder(titleRight); rightScroller.setBorder(borderRight); - borderRight.setTitleColor(java.awt.Color.WHITE); rightTable.setRowHeight(80); rightScroller.setViewportView(rightTable); diff --git a/src/main/java/pulse/ui/components/panels/LogToolbar.java b/src/main/java/pulse/ui/components/panels/LogToolbar.java index a9fd6a1..84879f1 100644 --- a/src/main/java/pulse/ui/components/panels/LogToolbar.java +++ b/src/main/java/pulse/ui/components/panels/LogToolbar.java @@ -1,7 +1,5 @@ package pulse.ui.components.panels; -import static pulse.tasks.logs.Log.isVerbose; -import static pulse.tasks.logs.Log.setVerbose; import static pulse.ui.Messages.getString; import static pulse.util.ImageUtils.loadIcon; @@ -14,13 +12,15 @@ import javax.swing.JCheckBox; import javax.swing.JToolBar; -import pulse.ui.components.listeners.LogExportListener; +import static pulse.tasks.logs.Log.setGraphicalLog; +import static pulse.tasks.logs.Log.isGraphicalLog; +import pulse.ui.components.listeners.LogListener; @SuppressWarnings("serial") public class LogToolbar extends JToolBar { private final static int ICON_SIZE = 16; - private List listeners; + private List listeners; public LogToolbar() { super(); @@ -35,23 +35,24 @@ public void initComponents() { var saveLogBtn = new JButton(loadIcon("save.png", ICON_SIZE, Color.white)); saveLogBtn.setToolTipText("Save"); - var verboseCheckBox = new JCheckBox(getString("LogToolBar.Verbose")); //$NON-NLS-1$ - verboseCheckBox.setSelected(isVerbose()); - verboseCheckBox.setHorizontalAlignment(CENTER); + var logmodeCheckbox = new JCheckBox(getString("LogToolBar.Verbose")); //$NON-NLS-1$ + logmodeCheckbox.setSelected(isGraphicalLog()); + logmodeCheckbox.setHorizontalAlignment(CENTER); - verboseCheckBox.addActionListener(event -> setVerbose(verboseCheckBox.isSelected())); + logmodeCheckbox.addActionListener(event -> { + boolean selected = logmodeCheckbox.isSelected(); + setGraphicalLog(selected); + listeners.stream().forEach(l -> l.onLogModeChanged(selected)); + }); - saveLogBtn.addActionListener(e -> notifyLog()); + saveLogBtn.addActionListener(e -> listeners.stream().forEach(l -> l.onLogExportRequest())); add(saveLogBtn); - add(verboseCheckBox); - } - public void notifyLog() { - listeners.stream().forEach(l -> l.onLogExportRequest()); + add(logmodeCheckbox); } - public void addLogExportListener(LogExportListener l) { + public void addLogListener(LogListener l) { listeners.add(l); } diff --git a/src/main/java/pulse/ui/components/panels/ModelToolbar.java b/src/main/java/pulse/ui/components/panels/ModelToolbar.java index 7c0b550..770734e 100644 --- a/src/main/java/pulse/ui/components/panels/ModelToolbar.java +++ b/src/main/java/pulse/ui/components/panels/ModelToolbar.java @@ -18,7 +18,7 @@ @SuppressWarnings("serial") public class ModelToolbar extends JToolBar { - private final static int ICON_SIZE = 20; + private final static int ICON_SIZE = 16; public ModelToolbar() { super(); diff --git a/src/main/java/pulse/ui/components/panels/ProblemToolbar.java b/src/main/java/pulse/ui/components/panels/ProblemToolbar.java index 92e42ca..0ddf940 100644 --- a/src/main/java/pulse/ui/components/panels/ProblemToolbar.java +++ b/src/main/java/pulse/ui/components/panels/ProblemToolbar.java @@ -5,15 +5,12 @@ import static javax.swing.JOptionPane.ERROR_MESSAGE; import static javax.swing.JOptionPane.showMessageDialog; import static javax.swing.SwingUtilities.getWindowAncestor; -import static pulse.input.InterpolationDataset.StandartType.DENSITY; -import static pulse.input.InterpolationDataset.StandartType.HEAT_CAPACITY; import static pulse.tasks.logs.Status.INCOMPLETE; import static pulse.ui.Messages.getString; import java.awt.Component; import java.awt.GridLayout; import java.awt.event.ActionEvent; -import java.util.concurrent.Executors; import javax.swing.JButton; import javax.swing.JToolBar; @@ -23,6 +20,8 @@ import pulse.tasks.Calculation; import pulse.tasks.TaskManager; import pulse.ui.components.buttons.LoaderButton; +import static pulse.ui.components.buttons.LoaderButton.DataType.DENSITY; +import static pulse.ui.components.buttons.LoaderButton.DataType.HEAT_CAPACITY; import pulse.ui.frames.MainGraphFrame; import pulse.ui.frames.TaskControlFrame; @@ -41,12 +40,12 @@ public ProblemToolbar() { btnSimulate = new JButton(getString("ProblemStatementFrame.SimulateButton")); //$NON-NLS-1$ add(btnSimulate); - btnLoadCv = new LoaderButton(getString("ProblemStatementFrame.LoadSpecificHeatButton")); //$NON-NLS-1$ - btnLoadCv.setDataType(HEAT_CAPACITY); + btnLoadCv = new LoaderButton(HEAT_CAPACITY, + getString("ProblemStatementFrame.LoadSpecificHeatButton")); //$NON-NLS-1$ add(btnLoadCv); - btnLoadDensity = new LoaderButton(getString("ProblemStatementFrame.LoadDensityButton")); //$NON-NLS-1$ - btnLoadDensity.setDataType(DENSITY); + btnLoadDensity = new LoaderButton(DENSITY, + getString("ProblemStatementFrame.LoadDensityButton")); //$NON-NLS-1$ add(btnLoadDensity); btnSimulate.addActionListener((ActionEvent e) @@ -64,13 +63,13 @@ public static void plot(ActionEvent e) { var calc = (Calculation) t.getResponse(); - t.checkProblems(true); + t.checkProblems(); var status = t.getStatus(); if (status == INCOMPLETE && !status.checkProblemStatementSet()) { getDefaultToolkit().beep(); - showMessageDialog(getWindowAncestor((Component) e.getSource()), + showMessageDialog(getWindowAncestor((Component) e.getSource()), calc.getStatus().getMessage(), getString("ProblemStatementFrame.ErrorTitle"), //$NON-NLS-1$ ERROR_MESSAGE); diff --git a/src/main/java/pulse/ui/components/panels/SettingsToolBar.java b/src/main/java/pulse/ui/components/panels/SettingsToolBar.java index 405e545..a86cee5 100644 --- a/src/main/java/pulse/ui/components/panels/SettingsToolBar.java +++ b/src/main/java/pulse/ui/components/panels/SettingsToolBar.java @@ -19,8 +19,6 @@ public class SettingsToolBar extends JToolBar { - private static final long serialVersionUID = -1171612225785102673L; - private JCheckBox cbSingleStatement, cbHideDetails; public SettingsToolBar(PropertyHolderTable... tables) { diff --git a/src/main/java/pulse/ui/components/panels/TaskToolbar.java b/src/main/java/pulse/ui/components/panels/TaskToolbar.java index 786178e..522a6f3 100644 --- a/src/main/java/pulse/ui/components/panels/TaskToolbar.java +++ b/src/main/java/pulse/ui/components/panels/TaskToolbar.java @@ -68,6 +68,10 @@ private void initComponents() { execBtn.setToolTipText("Execute All Tasks"); add(execBtn); } + + public void resetSession() { + ((ExecutionButton)execBtn).resetSession(); + } public void setRemoveEnabled(boolean b) { removeBtn.setEnabled(b); @@ -118,5 +122,11 @@ public void notifyGraph() { public void addTaskActionListener(TaskActionListener l) { listeners.add(l); } + + public void removeListeners() { + if(listeners != null) { + listeners.clear(); + } + } } diff --git a/src/main/java/pulse/ui/frames/DataFrame.java b/src/main/java/pulse/ui/frames/DataFrame.java index 492df29..152b1a7 100644 --- a/src/main/java/pulse/ui/frames/DataFrame.java +++ b/src/main/java/pulse/ui/frames/DataFrame.java @@ -16,7 +16,6 @@ public class DataFrame extends JFrame { - private static final long serialVersionUID = 1L; private JPanel contentPane; private PropertyHolderTable dataTable; private Component ancestorFrame; diff --git a/src/main/java/pulse/ui/frames/LogFrame.java b/src/main/java/pulse/ui/frames/LogFrame.java index 6410fb7..0db89c6 100644 --- a/src/main/java/pulse/ui/frames/LogFrame.java +++ b/src/main/java/pulse/ui/frames/LogFrame.java @@ -6,7 +6,6 @@ import static java.awt.GridBagConstraints.WEST; import static java.lang.System.err; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static javax.swing.SwingUtilities.getWindowAncestor; import static pulse.io.export.ExportManager.askToExport; import static pulse.tasks.listeners.TaskRepositoryEvent.State.TASK_ADDED; import static pulse.ui.Messages.getString; @@ -14,22 +13,25 @@ import java.awt.BorderLayout; import java.awt.GridBagConstraints; -import javax.swing.JFrame; import javax.swing.JInternalFrame; -import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; import pulse.tasks.TaskManager; import pulse.tasks.listeners.LogEntryListener; import pulse.tasks.logs.Log; import pulse.tasks.logs.LogEntry; -import pulse.ui.components.LogPane; +import pulse.tasks.logs.AbstractLogger; +import pulse.ui.components.GraphicalLogPane; +import pulse.ui.components.TextLogPane; +import pulse.ui.components.listeners.LogListener; import pulse.ui.components.panels.LogToolbar; import pulse.ui.components.panels.SystemPanel; -@SuppressWarnings("serial") public class LogFrame extends JInternalFrame { - private LogPane logTextPane; + private AbstractLogger logger; + private final static AbstractLogger graphical = new GraphicalLogPane(); + private final static AbstractLogger text = new TextLogPane(); public LogFrame() { super("Log", true, false, true, true); @@ -39,12 +41,10 @@ public LogFrame() { } private void initComponents() { - logTextPane = new LogPane(); - var logScroller = new JScrollPane(); - logScroller.setViewportView(logTextPane); + logger = Log.isGraphicalLog() ? graphical : text; getContentPane().setLayout(new BorderLayout()); - getContentPane().add(logScroller, CENTER); + getContentPane().add(logger.getGUIComponent(), CENTER); var gridBagConstraints = new GridBagConstraints(); gridBagConstraints.anchor = WEST; @@ -53,19 +53,32 @@ private void initComponents() { getContentPane().add(new SystemPanel(), PAGE_END); var logToolbar = new LogToolbar(); - logToolbar.addLogExportListener(() -> { - if (logTextPane.getDocument().getLength() > 0) { - askToExport(logTextPane, (JFrame) getWindowAncestor(this), - getString("LogToolBar.FileFormatDescriptor")); + + var lel = new LogListener() { + @Override + public void onLogExportRequest() { + if (logger == text) { + askToExport(logger, null, getString("LogToolBar.FileFormatDescriptor")); + } else { + System.out.println("To export the log entries, please switch to text mode first!"); + } } - }); + + @Override + public void onLogModeChanged(boolean graphical) { + SwingUtilities.invokeLater(() -> setGraphicalLogger(graphical)); + } + }; + + logToolbar.addLogListener(lel); getContentPane().add(logToolbar, NORTH); } - private void scheduleLogEvents() { + public void scheduleLogEvents() { var instance = TaskManager.getManagerInstance(); - instance.addSelectionListener(e -> logTextPane.printAll()); + instance.addSelectionListener( + e -> SwingUtilities.invokeLater(() -> logger.postAll())); instance.addTaskRepositoryListener(event -> { if (event.getState() != TASK_ADDED) { @@ -81,13 +94,12 @@ public void onLogFinished(Log log) { if (instance.getSelectedTask() == task) { try { - logTextPane.getUpdateExecutor().awaitTermination(10, MILLISECONDS); + logger.getUpdateExecutor().awaitTermination(10, MILLISECONDS); } catch (InterruptedException e) { err.println("Log not finished in time"); - e.printStackTrace(); } - logTextPane.printTimeTaken(log); + logger.printTimeTaken(log); } } @@ -95,7 +107,7 @@ public void onLogFinished(Log log) { @Override public void onNewEntry(LogEntry e) { if (instance.getSelectedTask() == task) { - logTextPane.callUpdate(); + logger.callUpdate(); } } @@ -105,8 +117,20 @@ public void onNewEntry(LogEntry e) { }); } - public LogPane getLogTextPane() { - return logTextPane; + public AbstractLogger getLogger() { + return logger; + } + + private void setGraphicalLogger(boolean graphicalLog) { + var old = logger; + logger = graphicalLog ? graphical : text; + + if (old != logger) { + getContentPane().remove(old.getGUIComponent()); + getContentPane().add(logger.getGUIComponent(), BorderLayout.CENTER); + SwingUtilities.invokeLater(() -> logger.postAll()); + } + } } diff --git a/src/main/java/pulse/ui/frames/MainGraphFrame.java b/src/main/java/pulse/ui/frames/MainGraphFrame.java index e4400d4..d31f570 100644 --- a/src/main/java/pulse/ui/frames/MainGraphFrame.java +++ b/src/main/java/pulse/ui/frames/MainGraphFrame.java @@ -4,11 +4,8 @@ import static java.awt.BorderLayout.LINE_END; import static java.awt.BorderLayout.PAGE_END; import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.swing.JInternalFrame; -import pulse.problem.schemes.solvers.SolverException; import pulse.tasks.TaskManager; import pulse.tasks.logs.Status; diff --git a/src/main/java/pulse/ui/frames/ModelSelectionFrame.java b/src/main/java/pulse/ui/frames/ModelSelectionFrame.java index 68856ab..1d24702 100644 --- a/src/main/java/pulse/ui/frames/ModelSelectionFrame.java +++ b/src/main/java/pulse/ui/frames/ModelSelectionFrame.java @@ -24,13 +24,17 @@ public ModelSelectionFrame() { setSize(new Dimension(400, 400)); setTitle("Stored Calculations"); getContentPane().add(new ModelToolbar(), BorderLayout.SOUTH); + resetSession(); + this.setDefaultCloseOperation(HIDE_ON_CLOSE); + } + + public void resetSession() { var instance = TaskManager.getManagerInstance(); instance.addTaskRepositoryListener(e -> { if (e.getState() == TASK_BROWSING_REQUEST) { table.update(instance.getTask(e.getId())); } }); - this.setDefaultCloseOperation(HIDE_ON_CLOSE); } } diff --git a/src/main/java/pulse/ui/frames/PreviewFrame.java b/src/main/java/pulse/ui/frames/PreviewFrame.java index 76a249d..87a156b 100644 --- a/src/main/java/pulse/ui/frames/PreviewFrame.java +++ b/src/main/java/pulse/ui/frames/PreviewFrame.java @@ -15,6 +15,7 @@ import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; +import static java.awt.Color.WHITE; import java.awt.GridLayout; import java.awt.Rectangle; import java.util.ArrayList; @@ -46,6 +47,7 @@ import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import pulse.tasks.processing.ResultFormat; +import pulse.ui.components.Chart; @SuppressWarnings("serial") public class PreviewFrame extends JInternalFrame { @@ -83,7 +85,7 @@ private void init() { getContentPane().add(createEmptyPanel(), CENTER); - var toolbar = new JToolBar(); + var toolbar = new JToolBar(); toolbar.setFloatable(false); toolbar.setLayout(new GridLayout()); @@ -93,7 +95,6 @@ private void init() { toolbar.add(selectX); selectXBox = new JComboBox<>(); - selectXBox.setFont(selectXBox.getFont().deriveFont(11)); toolbar.add(selectXBox); toolbar.add(new JSeparator()); @@ -102,7 +103,6 @@ private void init() { toolbar.add(selectY); selectYBox = new JComboBox<>(); - selectYBox.setFont(selectYBox.getFont().deriveFont(11)); toolbar.add(selectYBox); var drawSmoothBtn = new JToggleButton(); @@ -118,7 +118,7 @@ private void init() { selectXBox.addItemListener(e -> replot(chart)); selectYBox.addItemListener(e -> replot(chart)); - this.setDefaultCloseOperation(EXIT_ON_CLOSE); + this.setDefaultCloseOperation(EXIT_ON_CLOSE); } private void replot(JFreeChart chart) { @@ -146,7 +146,7 @@ private void replot(JFreeChart chart) { if (drawSmooth) { drawSmooth(plot, selectedX, selectedY); } - + } private void drawSmooth(XYPlot plot, int selectedX, int selectedY) { @@ -161,7 +161,7 @@ private void drawSmooth(XYPlot plot, int selectedX, int selectedY) { //Akima spline for small number of points var interpolator = new AkimaSplineInterpolator(); interpolation = interpolator.interpolate(data[selectedX][0], data[selectedY][0]); - } catch( NonMonotonicSequenceException e) { + } catch (NonMonotonicSequenceException e) { //do not draw if points not strictly increasing return; } @@ -175,7 +175,7 @@ private void drawSmooth(XYPlot plot, int selectedX, int selectedY) { x[i] = data[selectedX][0][0] + dx * i; try { y[i] = interpolation.value(x[i]); - } catch(OutOfRangeException e) { + } catch (OutOfRangeException e) { y[i] = Double.NaN; } } @@ -192,14 +192,14 @@ public void update(ResultFormat fmt, double[][][] data) { propertyNames = new ArrayList<>(size); String tmp; - + selectXBox.removeAllItems(); selectYBox.removeAllItems(); for (var s : descriptors) { selectXBox.addItem(s); selectYBox.addItem(s); - } + } selectXBox.setSelectedIndex(fmt.indexOf(TEST_TEMPERATURE)); selectYBox.setSelectedIndex(fmt.indexOf(DIFFUSIVITY)); @@ -228,11 +228,13 @@ private static ChartPanel createEmptyPanel() { //plot.setRangeGridlinesVisible(false); //plot.setDomainGridlinesVisible(false); + var fore = UIManager.getColor("Label.foreground"); + plot.setDomainGridlinePaint(fore); plot.getRenderer(1).setSeriesPaint(1, SMOOTH_COLOR); plot.getRenderer(0).setSeriesPaint(0, RESULT_COLOR); - plot.getRenderer(0).setSeriesShape(0, - new Rectangle(-MARKER_SIZE/2, -MARKER_SIZE/2, MARKER_SIZE, MARKER_SIZE)); + plot.getRenderer(0).setSeriesShape(0, + new Rectangle(-MARKER_SIZE / 2, -MARKER_SIZE / 2, MARKER_SIZE, MARKER_SIZE)); chart.removeLegend(); @@ -244,7 +246,10 @@ private static ChartPanel createEmptyPanel() { cp.setMinimumDrawHeight(10); chart.setBackgroundPaint(UIManager.getColor("Panel.background")); - + plot.setBackgroundPaint(chart.getBackgroundPaint()); + Chart.setAxisFontColor(plot.getDomainAxis(), fore); + Chart.setAxisFontColor(plot.getRangeAxis(), fore); + return cp; } diff --git a/src/main/java/pulse/ui/frames/ProblemStatementFrame.java b/src/main/java/pulse/ui/frames/ProblemStatementFrame.java index 6acd7da..9c0bd4b 100644 --- a/src/main/java/pulse/ui/frames/ProblemStatementFrame.java +++ b/src/main/java/pulse/ui/frames/ProblemStatementFrame.java @@ -14,14 +14,10 @@ import java.awt.BorderLayout; import java.awt.GridLayout; -import java.util.Arrays; import java.util.List; -import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import javax.swing.DefaultListModel; @@ -41,7 +37,6 @@ import pulse.tasks.Calculation; import pulse.tasks.SearchTask; import pulse.tasks.TaskManager; -import pulse.tasks.listeners.TaskSelectionEvent; import pulse.ui.components.ProblemTree; import pulse.ui.components.PropertyHolderTable; import pulse.ui.components.listeners.ProblemSelectionEvent; @@ -95,8 +90,6 @@ public ProblemStatementFrame() { problemTree = new ProblemTree(knownProblems); contentPane.add(new JScrollPane(problemTree)); - var instance = getManagerInstance(); - problemListExecutor = Executors.newCachedThreadPool(); schemeListExecutor = Executors.newCachedThreadPool(); propertyExecutor = Executors.newCachedThreadPool(); @@ -192,12 +185,12 @@ public void setSelectionPath(TreePath path) { getContentPane().add(contentPane, CENTER); getContentPane().add(toolbar, SOUTH); - /* - * listeners - */ - instance.addSelectionListener((TaskSelectionEvent e) -> update(instance.getSelectedTask())); - - getManagerInstance().addHierarchyListener(event -> { + resetSession(); + } + + public void resetSession() { + var instance = getManagerInstance(); + instance.addHierarchyListener(event -> { if ((event.getSource() instanceof PropertyHolderTable) && instance.isSingleStatement()) { //for all tasks @@ -215,7 +208,6 @@ public void setSelectionPath(TreePath path) { } }); - } public void update() { @@ -247,98 +239,62 @@ private void changeSchemes(DifferenceScheme newScheme) { var instance = TaskManager.getManagerInstance(); var selectedTask = instance.getSelectedTask(); - var schemeLoaderTracker = new ProgressDialog(); - schemeLoaderTracker.setTitle("Initialising solution schemes..."); - schemeLoaderTracker.setLocationRelativeTo(null); - schemeLoaderTracker.setAlwaysOnTop(true); + var tracker = new ProgressDialog(); + tracker.setTitle("Initialising solution schemes..."); + tracker.setLocationRelativeTo(null); + tracker.setAlwaysOnTop(true); - List> callableList; + tracker.trackProgress(instance.isSingleStatement() ? instance.getTaskList().size() : 1); + + Runnable finishingTouch = () -> { + var c = (Calculation) selectedTask.getResponse(); + schemeTable.setPropertyHolder(c.getScheme()); + if (c.getProblem().getComplexity() == HIGH) { + showMessageDialog(null, getString("complexity.warning"), + "High complexity", INFORMATION_MESSAGE); + } + ProblemToolbar.plot(null); + tracker.setVisible(false); + problemTable.requestFocus(); + }; if (instance.isSingleStatement()) { - callableList = instance.getTaskList().stream().map(t -> new Callable() { + var runnables = instance.getTaskList().stream().map(t -> new Runnable() { @Override - public DifferenceScheme call() throws Exception { + public void run() { changeScheme(t, newScheme); - schemeLoaderTracker.incrementProgress(); - return ((Calculation) t.getResponse()).getScheme(); + tracker.incrementProgress(); } }).collect(Collectors.toList()); + CompletableFuture.runAsync(() + -> runnables.parallelStream().forEach(c -> c.run())) + .thenRun(finishingTouch); + } else { - callableList = Arrays.asList(() -> { - changeScheme(selectedTask, newScheme); - return selectedTask.getResponse().getScheme(); - }); + CompletableFuture.runAsync(() -> changeScheme(selectedTask, newScheme)).thenRun(finishingTouch); } - schemeLoaderTracker.trackProgress(callableList.size() - 1); - - CompletableFuture.runAsync(() -> { - try { - schemeListExecutor.invokeAll(callableList); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - }).thenRun(() -> { - - var c = (Calculation) selectedTask.getResponse(); - schemeTable.setPropertyHolder(c.getScheme()); - if (c.getProblem().getComplexity() == HIGH) { - showMessageDialog(null, getString("complexity.warning"), - "High complexity", INFORMATION_MESSAGE); - } - Executors.newSingleThreadExecutor().submit(() -> ProblemToolbar.plot(null)); - }); } private void changeProblems(Problem newlySelectedProblem, Object source) { var instance = TaskManager.getManagerInstance(); - var task = instance.getSelectedTask(); - var selectedCalc = ((Calculation) task.getResponse()); - - var problemLoaderTracker = new ProgressDialog(); - problemLoaderTracker.setTitle("Changing problem statements..."); - problemLoaderTracker.setLocationRelativeTo(null); - problemLoaderTracker.setAlwaysOnTop(true); + var selectedProblem = instance.getSelectedTask(); - List> callableList; + var tracker = new ProgressDialog(); + tracker.setTitle("Changing problem statements..."); + tracker.setLocationRelativeTo(null); + tracker.setAlwaysOnTop(true); if (source != instance) { - //apply to all tasks - if (instance.isSingleStatement()) { - callableList = instance.getTaskList().stream().map(t -> new Callable() { - @Override - public Problem call() throws Exception { - changeProblem(t, newlySelectedProblem); - var result = ((Calculation) t.getResponse()).getProblem(); - problemLoaderTracker.incrementProgress(); - return result; - } + tracker.trackProgress(instance.isSingleStatement() ? instance.getTaskList().size() : 1); - }).collect(Collectors.toList()); - - } //apply only to this task - else { - callableList = Arrays.asList(() -> { - changeProblem(task, newlySelectedProblem); - return ((Calculation) task.getResponse()).getProblem(); - }); - } - - problemLoaderTracker.trackProgress(callableList.size() - 1); - - CompletableFuture.runAsync(() -> { - try { - problemListExecutor.invokeAll(callableList); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - } - ).thenRun(() -> { + Runnable finishingTouch = () -> { + var selectedCalc = (Calculation) selectedProblem.getResponse(); problemTable.setPropertyHolder(selectedCalc.getProblem()); // after problem is selected for this task, show available difference schemes var defaultModel = (DefaultListModel) (schemeSelectionList.getModel()); @@ -347,7 +303,27 @@ public Problem call() throws Exception { schemes.forEach(s -> defaultModel.addElement(s)); selectDefaultScheme(schemeSelectionList, selectedCalc.getProblem()); schemeSelectionList.setToolTipText(null); - }); + tracker.setVisible(false); + }; + + if (instance.isSingleStatement()) { + + var runnables = instance.getTaskList().stream().map(t -> new Runnable() { + @Override + public void run() { + changeProblem(t, newlySelectedProblem); + tracker.incrementProgress(); + } + + }).collect(Collectors.toList()); + + CompletableFuture.runAsync(() + -> runnables.parallelStream().forEach(c -> c.run())) + .thenRun(finishingTouch); + + } else { + CompletableFuture.runAsync(() -> changeProblem(selectedProblem, newlySelectedProblem)).thenRun(finishingTouch); + } } @@ -372,7 +348,7 @@ private void changeProblem(SearchTask task, Problem newProblem) { np.retrieveData(data); } - task.checkProblems(true); + task.checkProblems(); toolbar.highlightButtons(!np.isReady()); } @@ -413,7 +389,7 @@ private void changeScheme(SearchTask task, DifferenceScheme newScheme) { } - task.checkProblems(true); + task.checkProblems(); } diff --git a/src/main/java/pulse/ui/frames/ResultFrame.java b/src/main/java/pulse/ui/frames/ResultFrame.java index 0703127..93aa7e8 100644 --- a/src/main/java/pulse/ui/frames/ResultFrame.java +++ b/src/main/java/pulse/ui/frames/ResultFrame.java @@ -122,13 +122,19 @@ public void addFrameCreationListener(PreviewFrameCreationListener l) { private void showInputDialog() { averageWindowDialog.setLocationRelativeTo(null); averageWindowDialog.setVisible(true); - averageWindowDialog.setConfirmAction(() -> - ((ResultTableModel)resultTable.getModel()) - .merge(averageWindowDialog.value().doubleValue())); + averageWindowDialog.setConfirmAction(() + -> ((ResultTableModel) resultTable.getModel()) + .merge(averageWindowDialog.value().doubleValue())); } public ResultTable getResultTable() { return resultTable; } -} + public void removeAllListeners() { + if (listeners != null) { + listeners.clear(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/frames/SearchOptionsFrame.java b/src/main/java/pulse/ui/frames/SearchOptionsFrame.java index 567c449..94306df 100644 --- a/src/main/java/pulse/ui/frames/SearchOptionsFrame.java +++ b/src/main/java/pulse/ui/frames/SearchOptionsFrame.java @@ -48,7 +48,6 @@ public class SearchOptionsFrame extends JInternalFrame { private final JTable rightTable; private final PathSolversList pathList; - private final static Font FONT = new Font(getString("PropertyHolderTable.FontName"), ITALIC, 16); private final static List pathSolvers = instancesOf(PathOptimiser.class); private final NumericPropertyKeyword[] mandatorySelection = new NumericPropertyKeyword[]{MAXTEMP}; @@ -89,11 +88,11 @@ public SearchOptionsFrame() { leftTable.setModel(new ParameterTableModel(false)); leftTable.setTableHeader(null); leftTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - + rightTable = new javax.swing.JTable(); rightTable.setTableHeader(null); rightTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - + var mainContainer = new DoubleTablePanel(leftTable, "All Parameters", rightTable, "Optimised Parameters"); getContentPane().add(pathListScroller, gbc); @@ -110,7 +109,7 @@ public SearchOptionsFrame() { tableScroller.setBorder( createTitledBorder("Select search variables and settings")); getContentPane().add(tableScroller, gbc); - + } public void update() { @@ -127,18 +126,18 @@ public void update() { leftTable.setAutoCreateRowSorter(true); leftTable.getRowSorter().toggleSortOrder(0); - + var rightTblModel = rightTable.getModel(); var activeTask = TaskManager.getManagerInstance().getSelectedTask(); //model for the flags list already created if (rightTblModel instanceof SelectedKeysModel) { var searchKeys = activeTask.activeParameters(); - ((ParameterTableModel)leftTable.getModel()).populateWithAllProperties(); + ((ParameterTableModel) leftTable.getModel()).populateWithAllProperties(); ((SelectedKeysModel) rightTblModel).update(searchKeys); } //Create a new model for the flags list else { - var c = (Calculation)activeTask.getResponse(); + var c = (Calculation) activeTask.getResponse(); if (c != null && c.getProblem() != null) { var searchKeys = activeTask.activeParameters(); rightTable.setModel(new SelectedKeysModel(searchKeys, mandatorySelection)); @@ -149,28 +148,28 @@ public void update() { rightTable.getModel().addTableModelListener(new TableModelListener() { private void updateFlag(TableModelEvent arg0, boolean value) { - var source = (NumericPropertyKeyword) - ( (SelectedKeysModel)rightTable.getModel() ) - .getElementAt(arg0.getFirstRow()); + var source = (NumericPropertyKeyword) ((SelectedKeysModel) rightTable.getModel()) + .getElementAt(arg0.getFirstRow()); var flag = new Flag(source); flag.setValue(value); PathOptimiser.getInstance().update(flag); } - + @Override public void tableChanged(TableModelEvent tme) { - if(tme.getType() == TableModelEvent.INSERT) + if (tme.getType() == TableModelEvent.INSERT) { updateFlag(tme, true); - else if(tme.getType() == TableModelEvent.DELETE) + } else if (tme.getType() == TableModelEvent.DELETE) { updateFlag(tme, false); + } } - + }); } } pathTable.updateTable(); - + } class PathSolversList extends JList { @@ -180,10 +179,6 @@ public PathSolversList() { super(); setModel(new AbstractListModel() { - /** - * - */ - private static final long serialVersionUID = -7683200230096704268L; @Override public int getSize() { @@ -196,7 +191,6 @@ public PathOptimiser getElementAt(int index) { } }); - setFont(FONT); setSelectionMode(SINGLE_SELECTION); setCellRenderer(new SearchListRenderer()); @@ -211,7 +205,7 @@ public PathOptimiser getElementAt(int index) { pathTable.setPropertyHolder(optimiser); for (var t : TaskManager.getManagerInstance().getTaskList()) { - t.checkProblems(true); + t.checkProblems(); } }); diff --git a/src/main/java/pulse/ui/frames/TaskControlFrame.java b/src/main/java/pulse/ui/frames/TaskControlFrame.java index f04f050..2c17b04 100644 --- a/src/main/java/pulse/ui/frames/TaskControlFrame.java +++ b/src/main/java/pulse/ui/frames/TaskControlFrame.java @@ -18,6 +18,7 @@ import java.awt.event.ComponentEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.util.Objects; import javax.swing.JDesktopPane; import javax.swing.JFrame; @@ -25,7 +26,6 @@ import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; -import pulse.problem.statements.Pulse; import pulse.tasks.Calculation; import pulse.tasks.TaskManager; import pulse.ui.Version; @@ -56,6 +56,8 @@ public class TaskControlFrame extends JFrame { private PulseMainMenu mainMenu; + private final static int ICON_SIZE = 16; + public static TaskControlFrame getInstance() { return instance; } @@ -73,6 +75,27 @@ private TaskControlFrame() { setIconImage(loadIcon("logo.png", 32).getImage()); addListeners(); setDefaultCloseOperation(EXIT_ON_CLOSE); + TaskManager.addSessionListener(() -> resetSession()); + } + + public void resetSession() { + Objects.requireNonNull(mainMenu, "Menu not created"); + var s = TaskManager.getManagerInstance(); + s.addSelectionListener(e -> graphFrame.plot()); + problemStatementFrame.resetSession(); + taskManagerFrame.resetSession(); + modelFrame.resetSession(); + resultsFrame.getResultTable().resetSession(); + logFrame.getLogger().clear(); + logFrame.scheduleLogEvents(); + mainMenu.reset(); + s.addTaskRepositoryListener(e + -> { + if (e.getState() == TASK_BROWSING_REQUEST) { + setModelSelectionFrameVisible(true); + } + } + ); } private void addListeners() { @@ -134,15 +157,6 @@ public void onSearchSettingsShowRequest() { } }); - var manager = TaskManager.getManagerInstance(); - manager.addTaskRepositoryListener(e - -> { - if (e.getState() == TASK_BROWSING_REQUEST) { - setModelSelectionFrameVisible(true); - } - } - ); - addResultFormatListener(rfe -> ((ResultTableModel) resultsFrame.getResultTable().getModel()) .changeFormat(rfe.getResultFormat())); @@ -157,13 +171,13 @@ public void onRemoveRequest() { @Override public void onClearRequest() { - logFrame.getLogTextPane().clear(); + logFrame.getLogger().clear(); resultsFrame.getResultTable().clear(); } @Override public void onResetRequest() { - logFrame.getLogTextPane().clear(); + logFrame.getLogger().clear(); resultsFrame.getResultTable().removeAll(); } @@ -188,26 +202,26 @@ private void initComponents() { setJMenuBar(mainMenu); logFrame = new LogFrame(); - logFrame.setFrameIcon(loadIcon("log.png", 20, Color.white)); + logFrame.setFrameIcon(loadIcon("log.png", ICON_SIZE, Color.white)); resultsFrame = new ResultFrame(); - resultsFrame.setFrameIcon(loadIcon("result.png", 20, Color.white)); + resultsFrame.setFrameIcon(loadIcon("result.png", ICON_SIZE, Color.white)); previewFrame = new PreviewFrame(); - previewFrame.setFrameIcon(loadIcon("preview.png", 20, Color.white)); + previewFrame.setFrameIcon(loadIcon("preview.png", ICON_SIZE, Color.white)); taskManagerFrame = new TaskManagerFrame(); - taskManagerFrame.setFrameIcon(loadIcon("task_manager.png", 20, Color.white)); + taskManagerFrame.setFrameIcon(loadIcon("task_manager.png", ICON_SIZE, Color.white)); graphFrame = MainGraphFrame.getInstance(); - graphFrame.setFrameIcon(loadIcon("curves.png", 20, Color.white)); + graphFrame.setFrameIcon(loadIcon("curves.png", ICON_SIZE, Color.white)); problemStatementFrame = new ProblemStatementFrame(); - problemStatementFrame.setFrameIcon(loadIcon("heat_problem.png", 20, Color.white)); + problemStatementFrame.setFrameIcon(loadIcon("heat_problem.png", ICON_SIZE, Color.white)); modelFrame = new ModelSelectionFrame(); - modelFrame.setFrameIcon(loadIcon("stored.png", 20, Color.white)); + modelFrame.setFrameIcon(loadIcon("stored.png", ICON_SIZE, Color.white)); searchOptionsFrame = new SearchOptionsFrame(); - searchOptionsFrame.setFrameIcon(loadIcon("optimiser.png", 20, Color.white)); + searchOptionsFrame.setFrameIcon(loadIcon("optimiser.png", ICON_SIZE, Color.white)); pulseFrame = new InternalGraphFrame("Pulse Shape", new PulseChart("Time (ms)", "Laser Power (a. u.)")); - pulseFrame.setFrameIcon(loadIcon("pulse.png", 20, Color.white)); + pulseFrame.setFrameIcon(loadIcon("pulse.png", ICON_SIZE, Color.white)); pulseFrame.setVisible(false); /* diff --git a/src/main/java/pulse/ui/frames/TaskManagerFrame.java b/src/main/java/pulse/ui/frames/TaskManagerFrame.java index 033edf9..a24f9c0 100644 --- a/src/main/java/pulse/ui/frames/TaskManagerFrame.java +++ b/src/main/java/pulse/ui/frames/TaskManagerFrame.java @@ -28,6 +28,12 @@ public TaskManagerFrame() { setVisible(true); } + public void resetSession() { + taskTable.resetSession(); + taskToolbar.resetSession(); + adjustEnabledControls(); + } + private void manageListeners() { taskToolbar.addTaskActionListener(new TaskActionListener() { @@ -62,21 +68,21 @@ private void initComponents() { taskToolbar = new TaskToolbar(); getContentPane().add(taskToolbar, PAGE_START); } + + private void enableIfNeeded() { + var ttm = (TaskTableModel) taskTable.getModel(); + + boolean enabled = ttm.getRowCount() > 0; + taskToolbar.setClearEnabled(enabled); + taskToolbar.setResetEnabled(enabled); + taskToolbar.setExecEnabled(enabled); + } private void adjustEnabledControls() { var ttm = (TaskTableModel) taskTable.getModel(); - ttm.addTableModelListener((TableModelEvent arg0) -> { - if (ttm.getRowCount() < 1) { - taskToolbar.setClearEnabled(false); - taskToolbar.setResetEnabled(false); - taskToolbar.setExecEnabled(false); - } else { - taskToolbar.setClearEnabled(true); - taskToolbar.setResetEnabled(true); - taskToolbar.setExecEnabled(true); - } - }); + enableIfNeeded(); + ttm.addTableModelListener((TableModelEvent arg0) -> enableIfNeeded() ); taskTable.getSelectionModel().addListSelectionListener((ListSelectionEvent arg0) -> { var selection = taskTable.getSelectedRows(); diff --git a/src/main/java/pulse/ui/frames/dialogs/ExportDialog.java b/src/main/java/pulse/ui/frames/dialogs/ExportDialog.java index ed0f7ee..bdfe610 100644 --- a/src/main/java/pulse/ui/frames/dialogs/ExportDialog.java +++ b/src/main/java/pulse/ui/frames/dialogs/ExportDialog.java @@ -85,11 +85,11 @@ private File directoryQuery() { var returnVal = fileChooser.showOpenDialog(this); File f = null; - + if (returnVal == APPROVE_OPTION) { dir = f = fileChooser.getSelectedFile(); } - + return f; } @@ -250,7 +250,7 @@ public void removeUpdate(DocumentEvent e) { var browseBtn = new JButton("Browse..."); browseBtn.addActionListener(e -> directoryField.setText(directoryQuery() - .getPath() + separator + projectName + separator) ); + .getPath() + separator + projectName + separator)); var exportBtn = new JButton("Export"); diff --git a/src/main/java/pulse/ui/frames/dialogs/ProgressDialog.java b/src/main/java/pulse/ui/frames/dialogs/ProgressDialog.java index 179ab1f..82f2cf9 100644 --- a/src/main/java/pulse/ui/frames/dialogs/ProgressDialog.java +++ b/src/main/java/pulse/ui/frames/dialogs/ProgressDialog.java @@ -6,10 +6,16 @@ import java.awt.Dimension; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.JDialog; import javax.swing.JProgressBar; import javax.swing.SwingWorker; +import pulse.util.Serializer; +import static pulse.util.Serializer.deserialize; @SuppressWarnings("serial") public class ProgressDialog extends JDialog implements PropertyChangeListener { @@ -84,4 +90,25 @@ protected void done() { progressWorker.execute(); } -} + public interface ProgressWorker { + + public default void work() { + var dialog = new ProgressDialog(); + dialog.setLocationRelativeTo(null); + dialog.trackProgress(1); + CompletableFuture.runAsync(() -> { + try { + action(); + } catch (Exception ex) { + Logger.getLogger(Serializer.class.getName()).log(Level.SEVERE, "Failed to load session", ex); + System.err.println("Failed to load session."); + } + }) + .thenRun(() -> dialog.incrementProgress()); + } + + public void action(); + + } + +} \ No newline at end of file diff --git a/src/main/java/pulse/ui/frames/dialogs/ResultChangeDialog.java b/src/main/java/pulse/ui/frames/dialogs/ResultChangeDialog.java index b0554c3..b196587 100644 --- a/src/main/java/pulse/ui/frames/dialogs/ResultChangeDialog.java +++ b/src/main/java/pulse/ui/frames/dialogs/ResultChangeDialog.java @@ -14,10 +14,6 @@ public class ResultChangeDialog extends JDialog { - /** - * - */ - private static final long serialVersionUID = 1L; private final static int WIDTH = 1000; private final static int HEIGHT = 600; @@ -51,34 +47,36 @@ private void initComponents() { setDefaultCloseOperation(HIDE_ON_CLOSE); leftTbl = new javax.swing.JTable() { - + @Override - public boolean isCellEditable(int row, int column) { - return false; - }; - + public boolean isCellEditable(int row, int column) { + return false; + } + ; + }; leftTbl.setModel(new ParameterTableModel(true)); leftTbl.setTableHeader(null); rightTbl = new javax.swing.JTable() { - + @Override - public boolean isCellEditable(int row, int column) { - return false; - }; - + public boolean isCellEditable(int row, int column) { + return false; + } + ; + }; rightTbl.setModel(new SelectedKeysModel( ResultFormat.getInstance().getKeywords(), ResultFormat.getMinimalArray())); rightTbl.setTableHeader(null); - + MainContainer = new DoubleTablePanel(leftTbl, "All Parameters", rightTbl, "Output"); - + getContentPane().add(MainContainer, BorderLayout.CENTER); MainToolbar.setFloatable(false); diff --git a/src/main/java/pulse/util/Accessible.java b/src/main/java/pulse/util/Accessible.java index b9cdf8c..9cc2832 100644 --- a/src/main/java/pulse/util/Accessible.java +++ b/src/main/java/pulse/util/Accessible.java @@ -101,17 +101,18 @@ public List genericProperties() { var methods = this.getClass().getMethods(); for (var m : methods) { - if (m.getParameterCount() > 0) { - continue; - } + //getters only + if (m.getParameterCount() == 0) { - if (Property.class.isAssignableFrom(m.getReturnType()) - && !NumericProperty.class.isAssignableFrom(m.getReturnType())) + if (Property.class.isAssignableFrom(m.getReturnType()) + && !NumericProperty.class.isAssignableFrom(m.getReturnType())) try { - fields.add((Property) m.invoke(this)); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - err.println("Error invoking method " + m); - e.printStackTrace(); + fields.add((Property) m.invoke(this)); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + err.println("Error invoking method " + m); + e.printStackTrace(); + } + } } diff --git a/src/main/java/pulse/util/DescriptorChangeListener.java b/src/main/java/pulse/util/DescriptorChangeListener.java index 4c522b7..4edbe51 100644 --- a/src/main/java/pulse/util/DescriptorChangeListener.java +++ b/src/main/java/pulse/util/DescriptorChangeListener.java @@ -1,6 +1,8 @@ package pulse.util; -public interface DescriptorChangeListener { +import java.io.Serializable; + +public interface DescriptorChangeListener extends Serializable { public void onDescriptorChanged(); diff --git a/src/main/java/pulse/util/FunctionSerializer.java b/src/main/java/pulse/util/FunctionSerializer.java new file mode 100644 index 0000000..65911a8 --- /dev/null +++ b/src/main/java/pulse/util/FunctionSerializer.java @@ -0,0 +1,31 @@ +package pulse.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import org.apache.commons.math3.analysis.polynomials.PolynomialFunction; +import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction; + +public class FunctionSerializer { + + private FunctionSerializer() { + //empty + } + + public static void writeSplineFunction(PolynomialSplineFunction f, ObjectOutputStream oos) + throws IOException { + // write the object + double[] knots = f != null ? f.getKnots() : null; + PolynomialFunction[] funcs = f != null ? f.getPolynomials() : null; + oos.writeObject(knots); + oos.writeObject(funcs); + } + + public static PolynomialSplineFunction readSplineFunction(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + var knots = (double[]) ois.readObject(); // knots + var funcs = (PolynomialFunction[]) ois.readObject(); + return knots != null & funcs != null ? new PolynomialSplineFunction(knots, funcs) : null; + } + +} diff --git a/src/main/java/pulse/util/Group.java b/src/main/java/pulse/util/Group.java index 2b1f888..cbdfc35 100644 --- a/src/main/java/pulse/util/Group.java +++ b/src/main/java/pulse/util/Group.java @@ -25,8 +25,8 @@ public List subgroups() { var methods = this.getClass().getMethods(); for (var m : methods) { - - if (m.getParameterCount() > 0 + + if (m.getParameterCount() > 0 || !Group.class.isAssignableFrom(m.getReturnType()) || m.getReturnType().isAssignableFrom(getClass())) { continue; diff --git a/src/main/java/pulse/util/HierarchyListener.java b/src/main/java/pulse/util/HierarchyListener.java index c6c3ef8..c5a87c6 100644 --- a/src/main/java/pulse/util/HierarchyListener.java +++ b/src/main/java/pulse/util/HierarchyListener.java @@ -1,5 +1,7 @@ package pulse.util; +import java.io.Serializable; + /** * An hierarchy listener, which listens to any changes happening with the * children of an {@code UpwardsNavigable}. @@ -7,7 +9,7 @@ * @see pulse.util.UpwardsNavigable * */ -public interface HierarchyListener { +public interface HierarchyListener extends Serializable { /** * This is invoked by the {@code UpwardsNavigable} when an event resulting diff --git a/src/main/java/pulse/util/ImmutableDataEntry.java b/src/main/java/pulse/util/ImmutableDataEntry.java index 7a55587..b764829 100644 --- a/src/main/java/pulse/util/ImmutableDataEntry.java +++ b/src/main/java/pulse/util/ImmutableDataEntry.java @@ -1,5 +1,7 @@ package pulse.util; +import java.io.Serializable; + /** * A {@code DataEntry} is an immutable ordered pair of an instance of {@code T}, * which is considered to be the 'key', and an instance of {@code R}, which is @@ -8,7 +10,7 @@ * @param the key * @param the value */ -public class ImmutableDataEntry { +public class ImmutableDataEntry implements Serializable { private T key; private R value; diff --git a/src/main/java/pulse/util/ImmutablePair.java b/src/main/java/pulse/util/ImmutablePair.java index f264614..f01f9a4 100644 --- a/src/main/java/pulse/util/ImmutablePair.java +++ b/src/main/java/pulse/util/ImmutablePair.java @@ -1,6 +1,8 @@ package pulse.util; -public class ImmutablePair { +import java.io.Serializable; + +public class ImmutablePair implements Serializable { private T anElement; private T anotherElement; diff --git a/src/main/java/pulse/util/InstanceDescriptor.java b/src/main/java/pulse/util/InstanceDescriptor.java index bead937..a8769a5 100644 --- a/src/main/java/pulse/util/InstanceDescriptor.java +++ b/src/main/java/pulse/util/InstanceDescriptor.java @@ -30,7 +30,7 @@ public InstanceDescriptor(String generalDescriptor, Class c, Object... argume allDescriptors = nameMap.get(c); selectedDescriptor = allDescriptors.iterator().next(); this.generalDescriptor = generalDescriptor; - listeners = new ArrayList(); + listeners = new ArrayList<>(); } public InstanceDescriptor(Class c, Object... arguments) { @@ -50,13 +50,14 @@ public Object getValue() { @Override public boolean attemptUpdate(Object object) { var string = object.toString(); - + if (selectedDescriptor.equals(string)) { return false; } - - if(!allDescriptors.contains(string)) + + if (!allDescriptors.contains(string)) { throw new IllegalArgumentException("Unknown descriptor: " + selectedDescriptor); + } this.selectedDescriptor = string; listeners.stream().forEach(l -> l.onDescriptorChanged()); diff --git a/src/main/java/pulse/util/PropertyEvent.java b/src/main/java/pulse/util/PropertyEvent.java index 70c486a..0c74fc7 100644 --- a/src/main/java/pulse/util/PropertyEvent.java +++ b/src/main/java/pulse/util/PropertyEvent.java @@ -1,5 +1,6 @@ package pulse.util; +import java.io.Serializable; import pulse.properties.Property; /** @@ -7,7 +8,7 @@ * {@code PropertyHolder}. * */ -public class PropertyEvent { +public class PropertyEvent implements Serializable { private Object source; private PropertyHolder propertyHolder; diff --git a/src/main/java/pulse/util/PropertyHolder.java b/src/main/java/pulse/util/PropertyHolder.java index 05cef73..4eff7a0 100644 --- a/src/main/java/pulse/util/PropertyHolder.java +++ b/src/main/java/pulse/util/PropertyHolder.java @@ -22,7 +22,7 @@ public abstract class PropertyHolder extends Accessible { private List parameters = listedTypes(); - private List listeners; + private transient List listeners; private String prefix; /** @@ -63,8 +63,9 @@ public List listedTypes() { return listedKeywords().stream().map(key -> def(key)).collect(Collectors.toList()); } - public PropertyHolder() { - this.listeners = new ArrayList<>(); + public void initListeners() { + super.initListeners(); + listeners = new ArrayList<>(); } /** @@ -195,7 +196,9 @@ public boolean updateProperty(Object sourceComponent, Property updatedProperty) public void firePropertyChanged(Object source, Property property) { var event = new PropertyEvent(source, this, property); - listeners.forEach(l -> l.onPropertyChanged(event)); + if (listeners != null) { + listeners.forEach(l -> l.onPropertyChanged(event)); + } /* * If the changes are triggered by an external GUI component (such as @@ -220,11 +223,19 @@ public void updateProperties(Object sourceComponent, PropertyHolder propertyHold propertyHolder.data().stream().forEach(entry -> this.updateProperty(sourceComponent, entry)); } - public void removeHeatingCurveListeners() { - this.listeners.clear(); + public void removeListeners() { + if(listeners == null) { + listeners = new ArrayList<>(); + } + else { + listeners.clear(); + } } public void addListener(PropertyHolderListener l) { + if (listeners == null) { + this.listeners = new ArrayList<>(); + } this.listeners.add(l); } diff --git a/src/main/java/pulse/util/PropertyHolderListener.java b/src/main/java/pulse/util/PropertyHolderListener.java index de5017b..aa3e2b7 100644 --- a/src/main/java/pulse/util/PropertyHolderListener.java +++ b/src/main/java/pulse/util/PropertyHolderListener.java @@ -1,10 +1,12 @@ package pulse.util; +import java.io.Serializable; + /** * A listener used by {@code PropertyHolder}s to track changes with the * associated {@code Propert}ies. */ -public interface PropertyHolderListener { +public interface PropertyHolderListener extends Serializable { /** * This event is triggered by any {@code PropertyHolder}, the properties of diff --git a/src/main/java/pulse/util/Serializer.java b/src/main/java/pulse/util/Serializer.java new file mode 100644 index 0000000..ccd7833 --- /dev/null +++ b/src/main/java/pulse/util/Serializer.java @@ -0,0 +1,118 @@ +package pulse.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JFileChooser; +import static javax.swing.JFileChooser.APPROVE_OPTION; +import static javax.swing.JFileChooser.FILES_ONLY; +import javax.swing.filechooser.FileNameExtensionFilter; +import pulse.tasks.TaskManager; +import pulse.ui.frames.dialogs.ProgressDialog.ProgressWorker; + +public class Serializer { + + private static final FileNameExtensionFilter filter = new FileNameExtensionFilter( + "Saved sessions (.pulse)", "pulse"); + + private Serializer() { + // + } + + public static void serialize() throws IOException, FileNotFoundException, ClassNotFoundException { + var fileChooser = new JFileChooser(); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileSelectionMode(FILES_ONLY); + fileChooser.setFileFilter(filter); + File f = new File("./Saved/"); + if (!f.exists()) { + f.mkdir(); + } + fileChooser.setCurrentDirectory(f); + + int returnVal = fileChooser.showSaveDialog(null); + + if (returnVal == APPROVE_OPTION) { + String ext = filter.getExtensions()[0]; + File fileToBeSaved; + if (!fileChooser.getSelectedFile().getAbsolutePath().endsWith(ext)) { + fileToBeSaved = new File(fileChooser.getSelectedFile() + ext); + } else { + fileToBeSaved = fileChooser.getSelectedFile(); + } + + ProgressWorker worker = () -> { + try { + serialize(fileToBeSaved); + } catch (IOException | ClassNotFoundException ex) { + Logger.getLogger(Serializer.class.getName()).log(Level.SEVERE, "Failed to save session", ex); + System.err.println("Failed to save session."); + } + }; + + worker.work(); + } + + } + + public static void deserialize() throws FileNotFoundException { + var fileChooser = new JFileChooser(); + fileChooser.setMultiSelectionEnabled(false); + fileChooser.setFileSelectionMode(FILES_ONLY); + fileChooser.setFileFilter(filter); + File f = new File("./Saved/"); + if (f.exists()) { + fileChooser.setCurrentDirectory(f); + } + + int returnVal = fileChooser.showOpenDialog(null); + + if (returnVal == APPROVE_OPTION) { + + ProgressWorker worker = () -> { + try { + deserialize(fileChooser.getSelectedFile()); + } catch (IOException | ClassNotFoundException ex) { + Logger.getLogger(Serializer.class.getName()).log(Level.SEVERE, "Failed to load session", ex); + System.err.println("Failed to load session."); + } + }; + + worker.work(); + + } + + } + + public static void serialize(File fname) throws FileNotFoundException, IOException, ClassNotFoundException { + FileOutputStream fileOutputStream = new FileOutputStream(fname); + try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) { + var instance = TaskManager.getManagerInstance(); + objectOutputStream.writeObject(instance); + } + } + + public static void deserialize(File fname) throws FileNotFoundException, IOException, ClassNotFoundException { + FileInputStream fis = new FileInputStream(fname); + TaskManager state; + try (ObjectInputStream ois = new ObjectInputStream(fis)) { + state = (TaskManager) ois.readObject(); + } + //close stream + state.initListeners(); + state.getTaskList().stream().forEach(t -> { + t.initListeners(); + t.children().stream().forEach(c -> c.initListeners()); + } + ); + TaskManager.assumeNewState(state); + state.fireTaskSelected(state); + } + +} diff --git a/src/main/java/pulse/util/UpwardsNavigable.java b/src/main/java/pulse/util/UpwardsNavigable.java index ab8e744..1d4992c 100644 --- a/src/main/java/pulse/util/UpwardsNavigable.java +++ b/src/main/java/pulse/util/UpwardsNavigable.java @@ -1,5 +1,6 @@ package pulse.util; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -16,10 +17,14 @@ *

* */ -public abstract class UpwardsNavigable implements Descriptive { +public abstract class UpwardsNavigable implements Descriptive, Serializable { private UpwardsNavigable parent; - private final List listeners = new ArrayList<>(); + private transient List listeners; + + public void initListeners() { + listeners = new ArrayList<>(); + } public final void removeHierarchyListeners() { this.listeners.clear(); @@ -48,7 +53,9 @@ public final List getHierarchyListeners() { */ public void tellParent(PropertyEvent e) { if (parent != null) { - parent.listeners.forEach(l -> l.onChildPropertyChanged(e)); + if (parent.listeners != null) { + parent.listeners.forEach(l -> l.onChildPropertyChanged(e)); + } parent.tellParent(e); } } diff --git a/src/main/resources/NumericProperty.xml b/src/main/resources/NumericProperty.xml index edf471e..cfc9984 100644 --- a/src/main/resources/NumericProperty.xml +++ b/src/main/resources/NumericProperty.xml @@ -1,5 +1,10 @@ + + + minimum="1.0E-4" value="0.01" primitive-type="double" discreet="true" default-search-variable="false"> -
Time taken: LogToolBar.FileFormatDescriptor=HTML Log Files LogToolBar.SaveButton=Save Log -LogToolBar.Verbose=Verbose log +LogToolBar.Verbose=Graphical NumberEditor.EditText=Edit NumberEditor.IllegalTableEntry=Illegal table entry NumberEditor.InvalidText=Invalid Text Entered @@ -291,4 +291,4 @@ MixedScheme2.5=Increased Accuracy Semi-implicit Scheme (NL)

    TextWrap.1=

    TextWrap.2=

    -msg.running=An instance of PULsE appears to be running. Please switch back to the running version or delete the pulse.lock file found in the PULsE directory. \ No newline at end of file +msg.running=An instance of PULsE appears to be running. Please switch back to the running version or delete the pulse.lock file in the user home directory. \ No newline at end of file