diff --git a/pom.xml b/pom.xml index e27af65..ecbe713 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ kotik-coder PULsE - 1.94 + 1.95 PULsE Processing Unit for Laser flash Experiments diff --git a/src/main/java/pulse/AbstractData.java b/src/main/java/pulse/AbstractData.java index 2f58d47..1fd6264 100644 --- a/src/main/java/pulse/AbstractData.java +++ b/src/main/java/pulse/AbstractData.java @@ -8,13 +8,11 @@ import static pulse.properties.NumericPropertyKeyword.NUMPOINTS; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Set; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import pulse.properties.Property; import pulse.util.PropertyHolder; /** @@ -105,7 +103,7 @@ public void clear() { * @return a {@code NumericProperty} derived from * {@code NumericPropertyKeyword.NUMPOINTS} with the value of {@code count} */ - public NumericProperty getNumPoints() { + public final NumericProperty getNumPoints() { return derive(NUMPOINTS, count); } @@ -117,7 +115,7 @@ public NumericProperty getNumPoints() { * * @param c */ - public void setNumPoints(NumericProperty c) { + public final void setNumPoints(NumericProperty c) { requireType(c, NUMPOINTS); this.count = (int) c.getValue(); firePropertyChanged(this, c); @@ -168,7 +166,7 @@ public void addPoint(double time, double sgn) { this.signal.add(sgn); } - protected void incrementCount() { + protected final void incrementCount() { count++; } @@ -179,7 +177,7 @@ protected void incrementCount() { * @param index the index * @param t the new time value at this index */ - public void setTimeAt(int index, double t) { + public final void setTimeAt(int index, double t) { time.set(index, t); } @@ -190,7 +188,7 @@ public void setTimeAt(int index, double t) { * @param index the index * @param t the new signal value at this index */ - public void setSignalAt(int index, double t) { + public final void setSignalAt(int index, double t) { signal.set(index, t); } @@ -200,20 +198,10 @@ public void setSignalAt(int index, double t) { * @return the maximum signal value * @see java.util.Collections.max */ - public double apparentMaximum() { + public final double apparentMaximum() { return max(signal); } - /** - * Checks if the time list is incomplete. - * - * @return {@code false} if the list with time values has less elements than - * initially declared, {@code true} otherwise. - */ - public boolean isIncomplete() { - return time.size() < count; - } - @Override public String toString() { return name != null ? name : getClass().getSimpleName() + " (" + getNumPoints() + ")"; @@ -270,6 +258,10 @@ public void remove(int i) { public boolean ignoreSiblings() { return true; } + + public boolean isFull() { + return actualNumPoints() >= count; + } public List getTimeSequence() { return time; diff --git a/src/main/java/pulse/HeatingCurve.java b/src/main/java/pulse/HeatingCurve.java index 8111212..8fb0f55 100644 --- a/src/main/java/pulse/HeatingCurve.java +++ b/src/main/java/pulse/HeatingCurve.java @@ -22,7 +22,6 @@ import pulse.input.listeners.CurveEvent; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import pulse.properties.Property; /** * The {@code HeatingCurve} represents a time-temperature profile (a @@ -42,10 +41,12 @@ */ public class HeatingCurve extends AbstractData { - private List adjustedSignal; + private final List adjustedSignal; + private List lastCalculation; private double startTime; - private List listeners = new ArrayList(); + private final List listeners + = new ArrayList<>(); private UnivariateInterpolator splineInterpolator; private UnivariateFunction splineInterpolation; @@ -62,7 +63,7 @@ protected HeatingCurve(List time, List signal, final double star */ public HeatingCurve() { super(); - adjustedSignal = new ArrayList((int) this.getNumPoints().getValue()); + adjustedSignal = new ArrayList<>((int) this.getNumPoints().getValue()); splineInterpolator = new SplineInterpolator(); } @@ -102,10 +103,16 @@ public HeatingCurve(NumericProperty count) { splineInterpolator = new SplineInterpolator(); } + //TODO + public void copyToLastCalculation() { + lastCalculation = new ArrayList<>(0); + lastCalculation = new ArrayList<>(adjustedSignal); + } + @Override public void clear() { super.clear(); - this.adjustedSignal.clear(); + adjustedSignal.clear(); } /** @@ -128,6 +135,7 @@ public double timeAt(int index) { * @return a double, representing the baseline-corrected temperature at * {@code index} */ + @Override public double signalAt(int index) { return adjustedSignal.get(index); } @@ -154,7 +162,6 @@ public double signalAt(int index) { * @see pulse.input.listeners.CurveEvent */ public void scale(double scale) { - var signal = getSignalData(); final int count = this.actualNumPoints(); for (int i = 0; i < count; i++) { signal.set(i, signal.get(i) * scale); @@ -166,9 +173,8 @@ public void scale(double scale) { private void refreshInterpolation() { /* - * Prepare extended time array + * Prepare extended time array */ - var time = this.getTimeSequence(); var timeExtended = new double[time.size() + 1]; for (int i = 1; i < timeExtended.length; i++) { @@ -188,11 +194,12 @@ private void refreshInterpolation() { } final double alpha = -1.0; - adjustedSignalExtended[0] = alpha * adjustedSignalExtended[2] - (1.0 - alpha) * adjustedSignalExtended[1]; // extrapolate + adjustedSignalExtended[0] = alpha * adjustedSignalExtended[2] + - (1.0 - alpha) * adjustedSignalExtended[1]; // extrapolate // linearly /* - * Submit to spline interpolation + * Submit to spline interpolation */ splineInterpolation = splineInterpolator.interpolate(timeExtended, adjustedSignalExtended); } @@ -220,19 +227,24 @@ public double maxAdjustedSignal() { * heating curve. */ public void apply(Baseline baseline) { - var time = this.getTimeSequence(); - var signal = this.getSignalData(); adjustedSignal.clear(); - for (int i = 0, size = time.size(); i < size; i++) { - adjustedSignal.add(signal.get(i) + baseline.valueAt(timeAt(i))); - } + int size = time.size(); + + if (size > 0) { + + for (int i = 0; i < size; i++) { + adjustedSignal.add(signal.get(i) + baseline.valueAt(timeAt(i))); + } + + if (time.get(0) > -startTime) { + time.add(0, -startTime); + adjustedSignal.add(0, baseline.valueAt(-startTime)); + } + + refreshInterpolation(); - if (time.get(0) > -startTime) { - time.add(0, -startTime); - adjustedSignal.add(0, baseline.valueAt(-startTime)); } - refreshInterpolation(); } /** @@ -251,6 +263,7 @@ public void apply(Baseline baseline) { * * @param data the experimental data, with a time range broader than the * time range of this {@code HeatingCurve}. + * @param baseline * @return a new {@code HeatingCurve}, extended to match the time limits of * {@code data} */ @@ -266,10 +279,9 @@ public final HeatingCurve extendedTo(ExperimentalData data, Baseline baseline) { var baselineTime = data.getTimeSequence().stream().filter(t -> t < 0).collect(toList()); var baselineSignal = baselineTime.stream().map(bTime -> baseline.valueAt(bTime)).collect(toList()); - var time = this.getTimeSequence(); - baselineTime.addAll(time); - baselineSignal.addAll(adjustedSignal); + this.copyToLastCalculation(); + baselineSignal.addAll(lastCalculation); return new HeatingCurve(baselineTime, baselineSignal, startTime, getName()); } diff --git a/src/main/java/pulse/HeatingCurveListener.java b/src/main/java/pulse/HeatingCurveListener.java index dd2219f..7dd972c 100644 --- a/src/main/java/pulse/HeatingCurveListener.java +++ b/src/main/java/pulse/HeatingCurveListener.java @@ -10,6 +10,7 @@ public interface HeatingCurveListener { /** * Signals that a {@code CurveEvent} has occurred. + * @param event */ public void onCurveEvent(CurveEvent event); diff --git a/src/main/java/pulse/input/ExperimentalData.java b/src/main/java/pulse/input/ExperimentalData.java index bdbeabf..15776db 100644 --- a/src/main/java/pulse/input/ExperimentalData.java +++ b/src/main/java/pulse/input/ExperimentalData.java @@ -19,7 +19,6 @@ import pulse.input.listeners.DataEvent; import pulse.input.listeners.DataEventType; import pulse.input.listeners.DataListener; -import pulse.properties.NumericProperty; import pulse.ui.Messages; import pulse.util.PropertyHolderListener; @@ -79,15 +78,15 @@ public ExperimentalData() { } - public void addDataListener(DataListener listener) { + public final void addDataListener(DataListener listener) { dataListeners.add(listener); } - public void clearDataListener() { + public final void clearDataListener() { dataListeners.clear(); } - public void fireDataChanged(DataEvent dataEvent) { + public final void fireDataChanged(DataEvent dataEvent) { dataListeners.stream().forEach(l -> l.onDataChanged(dataEvent)); } @@ -98,7 +97,7 @@ public void fireDataChanged(DataEvent dataEvent) { * @see pulse.input.Range.reset() * @see pulse.input.IndexRange.reset() */ - public void resetRanges() { + public final void resetRanges() { indexRange.reset(getTimeSequence()); range.reset(indexRange, getTimeSequence()); } @@ -335,19 +334,6 @@ private void doSetMetadata() { range.updateMinimum(metadata.numericProperty(PULSE_WIDTH)); } - metadata.addListener(event -> { - - if (event.getProperty() instanceof NumericProperty) { - var p = (NumericProperty) event.getProperty(); - - if (p.getType() == PULSE_WIDTH) { - range.updateMinimum(metadata.numericProperty(PULSE_WIDTH)); - } - - } - - }); - } /** @@ -413,7 +399,6 @@ public void setRange(Range range) { } private void doSetRange() { - var time = getTimeSequence(); indexRange.set(time, range); addHierarchyListener(l -> { @@ -439,4 +424,4 @@ public double timeLimit() { return timeAt(indexRange.getUpperBound()); } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/input/InterpolationDataset.java b/src/main/java/pulse/input/InterpolationDataset.java index 3db3182..45f244e 100644 --- a/src/main/java/pulse/input/InterpolationDataset.java +++ b/src/main/java/pulse/input/InterpolationDataset.java @@ -5,7 +5,7 @@ import static pulse.properties.NumericPropertyKeyword.SPECIFIC_HEAT; import java.util.ArrayList; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; @@ -14,6 +14,7 @@ import pulse.input.listeners.ExternalDatasetListener; import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.EMISSIVITY; import pulse.util.ImmutableDataEntry; /** @@ -29,9 +30,10 @@ public class InterpolationDataset { private UnivariateFunction interpolation; - private List> dataset; - private static Map standartDatasets = new HashMap(); - private static List listeners = new ArrayList<>(); + 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}. @@ -121,6 +123,7 @@ public static List derivableProperties() { } if (list.contains(SPECIFIC_HEAT) && list.contains(DENSITY)) { list.add(CONDUCTIVITY); + list.add(EMISSIVITY); } return list; } diff --git a/src/main/java/pulse/input/Metadata.java b/src/main/java/pulse/input/Metadata.java index 7b650e8..cd9369c 100644 --- a/src/main/java/pulse/input/Metadata.java +++ b/src/main/java/pulse/input/Metadata.java @@ -1,7 +1,6 @@ package pulse.input; import static java.lang.System.lineSeparator; -import static pulse.properties.NumericProperties.def; import static pulse.properties.NumericPropertyKeyword.DETECTOR_GAIN; import static pulse.properties.NumericPropertyKeyword.DETECTOR_IRIS; import static pulse.properties.NumericPropertyKeyword.DIAMETER; @@ -20,6 +19,7 @@ import pulse.problem.laser.NumericPulseData; import pulse.problem.laser.PulseTemporalShape; import pulse.problem.laser.RectangularPulse; +import static pulse.properties.NumericProperties.derive; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; import static pulse.properties.NumericPropertyKeyword.FOV_OUTER; @@ -46,8 +46,8 @@ public class Metadata extends PropertyHolder implements Reflexive { private SampleName sampleName; private int externalID; - private InstanceDescriptor pulseDescriptor = new InstanceDescriptor( - "Pulse Shape Selector", PulseTemporalShape.class); + private InstanceDescriptor pulseDescriptor + = new InstanceDescriptor<>("Pulse Shape Selector", PulseTemporalShape.class); private NumericPulseData pulseData; @@ -64,7 +64,7 @@ public Metadata(NumericProperty temperature, int externalId) { sampleName = new SampleName(); setExternalID(externalId); pulseDescriptor.setSelectedDescriptor(RectangularPulse.class.getSimpleName()); - data = new TreeSet(); + data = new TreeSet<>(); set(TEST_TEMPERATURE, temperature); } @@ -115,15 +115,17 @@ public void setSampleName(SampleName sampleName) { this.sampleName = sampleName; } - public void setPulseData(NumericPulseData pulseData) { + public final void setPulseData(NumericPulseData pulseData) { this.pulseData = pulseData; + 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 */ - public NumericPulseData getPulseData() { + public final NumericPulseData getPulseData() { return pulseData; } @@ -270,4 +272,4 @@ public boolean equals(Object o) { } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/input/listeners/CurveEventType.java b/src/main/java/pulse/input/listeners/CurveEventType.java index 65b364e..3dcb5d0 100644 --- a/src/main/java/pulse/input/listeners/CurveEventType.java +++ b/src/main/java/pulse/input/listeners/CurveEventType.java @@ -18,6 +18,13 @@ public enum CurveEventType { * shifting it relative to the experimental data points) or by the search * procedure. */ - TIME_ORIGIN_CHANGED; + TIME_ORIGIN_CHANGED, + + /** + * 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 f2b8ca4..d42c1a2 100644 --- a/src/main/java/pulse/input/listeners/DataEvent.java +++ b/src/main/java/pulse/input/listeners/DataEvent.java @@ -1,6 +1,6 @@ package pulse.input.listeners; -import pulse.input.ExperimentalData; +import pulse.AbstractData; /** * A {@code DataEvent} is used to track changes happening with a @@ -10,7 +10,7 @@ public class DataEvent { private DataEventType type; - private ExperimentalData data; + private AbstractData data; /** * Constructs a {@code DataEvent} object, combining the {@code type} and @@ -19,7 +19,7 @@ public class DataEvent { * @param type the type of this event * @param data the source of the event */ - public DataEvent(DataEventType type, ExperimentalData data) { + public DataEvent(DataEventType type, AbstractData data) { this.type = type; this.data = data; } @@ -39,7 +39,7 @@ public DataEventType getType() { * * @return the associated data */ - public ExperimentalData getData() { + public AbstractData getData() { return data; } diff --git a/src/main/java/pulse/input/listeners/DataEventType.java b/src/main/java/pulse/input/listeners/DataEventType.java index 0423e37..da2160c 100644 --- a/src/main/java/pulse/input/listeners/DataEventType.java +++ b/src/main/java/pulse/input/listeners/DataEventType.java @@ -14,6 +14,12 @@ public enum DataEventType { * @see pulse.input.ExperimentalData.truncate() */ - RANGE_CHANGED + RANGE_CHANGED, + + /** + * All data points loaded and are ready for processing. + */ + + DATA_LOADED; } diff --git a/src/main/java/pulse/io/export/ResultTableExporter.java b/src/main/java/pulse/io/export/ResultTableExporter.java index f61631e..ea6bd54 100644 --- a/src/main/java/pulse/io/export/ResultTableExporter.java +++ b/src/main/java/pulse/io/export/ResultTableExporter.java @@ -2,13 +2,16 @@ import java.io.FileOutputStream; import java.io.PrintStream; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import pulse.properties.NumericProperty; -import pulse.properties.NumericPropertyKeyword; +import pulse.tasks.TaskManager; import pulse.tasks.processing.AbstractResult; import pulse.tasks.processing.AverageResult; import pulse.ui.Messages; +import pulse.ui.Version; import pulse.ui.components.ResultTable; import pulse.ui.components.models.ResultTableModel; @@ -61,26 +64,33 @@ public void printToStream(ResultTable table, FileOutputStream fos, Extension ext } private void printHeaderCSV(ResultTable table, PrintStream stream) { - NumericPropertyKeyword p = null; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + stream.println("Summary report on " + + LocalDateTime.now().format(formatter)); + stream.println("PULsE Version: " + Version.getCurrentVersion().toString()); + stream.println("Sample: " + TaskManager.getManagerInstance().getSampleName()); + stream.println("Ouput format sequence below: "); + + var fmt = ((ResultTableModel) table.getModel()).getFormat(); + for (int col = 0; col < table.getColumnCount(); col++) { - p = ((ResultTableModel) table.getModel()).getFormat().fromAbbreviation(table.getColumnName(col)); - stream.printf("%20s ", p); + var colName = fmt.fromAbbreviation(table.getColumnName(col)); + stream.println("Col. no.: " + col + " - " + colName); } - stream.println(""); + + stream.println("Note: average results are formatted as ; in the list below."); + stream.println(); } private void printIndividualCSV(NumericProperty p, PrintStream stream) { - if(p.getError() == null || p.getError().doubleValue() < 1E-20 ) { - if(p.getValue() instanceof Double) - stream.printf("%12.5e", p.valueInCurrentUnits()); - else - stream.printf("%12d", p.valueInCurrentUnits().intValue()); - } - else { - if(p.getValue() instanceof Double) - stream.printf("%12.5e +/- %12.5e", p.valueInCurrentUnits(), p.errorInCurrentUnits()); - else - stream.printf("%12d +/- %12d", p.valueInCurrentUnits().intValue(), p.errorInCurrentUnits().intValue()); + String fmt = p.getValue() instanceof Double ? "%-2.5e" : "%-6d"; + String s1 = String.format(fmt, p.getValue()).trim(); + String s2 = ""; + if (p.getError() != null) { + s2 = String.format(fmt, p.getError()).trim(); + stream.print(s1 + " ; " + s2 + " "); + } else { + stream.print(s1 + " "); } } @@ -104,8 +114,6 @@ private void printCSV(ResultTable table, FileOutputStream fos) { stream.print(Messages.getString("ResultTable.SeparatorCSV")); stream.println(""); - printHeaderCSV(table, stream); - results.stream().filter(r -> r instanceof AverageResult) .forEach(ar -> ((AverageResult) ar).getIndividualResults().stream().forEach(ir -> { var props = AbstractResult.filterProperties(ir); diff --git a/src/main/java/pulse/io/readers/NetzschCSVReader.java b/src/main/java/pulse/io/readers/NetzschCSVReader.java index dae82be..4445add 100644 --- a/src/main/java/pulse/io/readers/NetzschCSVReader.java +++ b/src/main/java/pulse/io/readers/NetzschCSVReader.java @@ -8,6 +8,7 @@ import java.io.FileReader; import java.io.IOException; import java.text.DecimalFormat; +import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; @@ -50,16 +51,22 @@ public class NetzschCSVReader implements CurveReader { /** * Note comma is included as a delimiter character here. */ - private final static String ENGLISH_DELIMS = "[#(),/°Cx%^]+"; + private final static String ENGLISH_DELIMS = "[#(),;/°Cx%^]+"; private final static String GERMAN_DELIMS = "[#();/°Cx%^]+"; - private static String delims = ENGLISH_DELIMS; - + private static String delims; //default number format (British format) - private static Locale locale = Locale.ENGLISH; + private static Locale locale; + + private static NumberFormat format; private NetzschCSVReader() { - //intentionally blank + //do nothing + } + + protected void setDefaultLocale() { + delims = ENGLISH_DELIMS; + locale = Locale.ENGLISH; } /** @@ -97,39 +104,39 @@ public String getSupportedExtension() { @Override public List read(File file) throws IOException { Objects.requireNonNull(file, Messages.getString("DATReader.1")); - ExperimentalData curve = new ExperimentalData(); + setDefaultLocale(); //always start with a default locale + //gets the number format for this locale try (BufferedReader reader = new BufferedReader(new FileReader(file))) { int shotId = determineShotID(reader, file); - var format = DecimalFormat.getInstance(locale); - format.setGroupingUsed(false); - - var spot = findLineByLabel(reader, DETECTOR_SPOT_SIZE, THICKNESS, delims); + String spot = findLineByLabel(reader, DETECTOR_SPOT_SIZE, THICKNESS, false); + double spotSize = 0; if(spot != null) { var spotTokens = spot.split(delims); spotSize = format.parse(spotTokens[spotTokens.length - 1]).doubleValue() * TO_METRES; } - var tempTokens = findLineByLabel(reader, THICKNESS, delims).split(delims); + String tempLine = findLineByLabel(reader, THICKNESS, false); + var tempTokens = tempLine.split(delims); final double thickness = format.parse(tempTokens[tempTokens.length - 1]).doubleValue() * TO_METRES; - tempTokens = findLineByLabel(reader, DIAMETER, delims).split(delims); + tempTokens = findLineByLabel(reader, DIAMETER, false).split(delims); final double diameter = format.parse(tempTokens[tempTokens.length - 1]).doubleValue() * TO_METRES; - tempTokens = findLineByLabel(reader, SAMPLE_TEMPERATURE, delims).split(delims); + tempTokens = findLineByLabel(reader, SAMPLE_TEMPERATURE, false).split(delims); final double sampleTemperature = format.parse(tempTokens[tempTokens.length - 1]).doubleValue() + TO_KELVIN; /* * Finds the detector keyword. */ - var detectorLabel = findLineByLabel(reader, DETECTOR, delims); + var detectorLabel = findLineByLabel(reader, DETECTOR, true); if (detectorLabel == null) { System.err.println("Skipping " + file.getName()); @@ -156,17 +163,41 @@ public List read(File file) throws IOException { return null; } + + /** + * Note: the {@code line} must contain a decimal-separated number. + * @param line a line containing number with a decimal separator + */ + + private static void guessLocaleAndFormat(String line) { + + if(line.contains(".")) { + delims = ENGLISH_DELIMS; + locale = Locale.ENGLISH; + } + + else { + delims = GERMAN_DELIMS; + locale = Locale.GERMAN; + } + + format = DecimalFormat.getInstance(locale); + format.setGroupingUsed(false); + } protected static void populate(AbstractData data, BufferedReader reader) throws IOException, ParseException { double time; double power; String[] tokens; - var format = DecimalFormat.getInstance(locale); - format.setGroupingUsed(false); for (String line = reader.readLine(); line != null && !line.trim().isEmpty(); line = reader.readLine()) { tokens = line.split(delims); - + + if(tokens.length < 2) { + guessLocaleAndFormat(line); + tokens = line.split(delims); + } + time = format.parse(tokens[0]).doubleValue() * NetzschCSVReader.TO_SECONDS; power = format.parse(tokens[1]).doubleValue(); data.addPoint(time, power); @@ -178,39 +209,25 @@ protected static int determineShotID(BufferedReader reader, File file) throws IO String shotIDLine = reader.readLine(); String[] shotID = shotIDLine.split(delims); - int shotId = -1; + int id; - if(shotID.length < 3) { - - if(locale == Locale.ENGLISH) { - delims = GERMAN_DELIMS; - locale = Locale.GERMAN; - } - else { - delims = ENGLISH_DELIMS; - locale = Locale.ENGLISH; - } - - shotID = shotIDLine.split(delims); - } - //check if first entry makes sense if (!shotID[shotID.length - 2].equalsIgnoreCase(SHOT_DATA)) { throw new IllegalArgumentException(file.getName() + " is not a recognised Netzch CSV file. First entry is: " + shotID[shotID.length - 2]); } else { - shotId = Integer.parseInt(shotID[shotID.length - 1]); + id = Integer.parseInt(shotID[shotID.length - 1]); } - return shotId; + return id; } - protected static String findLineByLabel(BufferedReader reader, String label, String delims) throws IOException { - return findLineByLabel(reader, label, "!!!", delims); + protected static String findLineByLabel(BufferedReader reader, String label, boolean ignoreLocale) throws IOException { + return findLineByLabel(reader, label, "!!!", ignoreLocale); } - protected static String findLineByLabel(BufferedReader reader, String label, String stopLabel, String delims) throws IOException { + protected static String findLineByLabel(BufferedReader reader, String label, String stopLabel, boolean ignoreLocale) throws IOException { String line = ""; String[] tokens; @@ -221,6 +238,11 @@ protected static String findLineByLabel(BufferedReader reader, String label, Str outer: for (line = reader.readLine(); line != null; line = reader.readLine()) { + if(line.isBlank()) + continue; + + if(!ignoreLocale) + guessLocaleAndFormat(line); tokens = line.split(delims); for (String token : tokens) { @@ -262,4 +284,4 @@ public static String getDelims() { return delims; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/io/readers/NetzschPulseCSVReader.java b/src/main/java/pulse/io/readers/NetzschPulseCSVReader.java index 4ba3303..7d759e6 100644 --- a/src/main/java/pulse/io/readers/NetzschPulseCSVReader.java +++ b/src/main/java/pulse/io/readers/NetzschPulseCSVReader.java @@ -55,13 +55,16 @@ public NumericPulseData read(File file) throws IOException { Objects.requireNonNull(file, Messages.getString("DATReader.1")); NumericPulseData data = null; + + ( (NetzschCSVReader) NetzschCSVReader.getInstance() ) + .setDefaultLocale(); //always start with a default locale try (BufferedReader reader = new BufferedReader(new FileReader(file))) { int shotId = NetzschCSVReader.determineShotID(reader, file); data = new NumericPulseData(shotId); - var pulseLabel = NetzschCSVReader.findLineByLabel(reader, PULSE, NetzschCSVReader.getDelims()); + var pulseLabel = NetzschCSVReader.findLineByLabel(reader, PULSE, false); if (pulseLabel == null) { System.err.println("Skipping " + file.getName()); diff --git a/src/main/java/pulse/io/readers/ReaderManager.java b/src/main/java/pulse/io/readers/ReaderManager.java index 97bc4a2..0f8201b 100644 --- a/src/main/java/pulse/io/readers/ReaderManager.java +++ b/src/main/java/pulse/io/readers/ReaderManager.java @@ -165,12 +165,12 @@ public static List datasetReaders() { /** * Attempts to find a {@code DatasetReader} for processing {@code file}. * + * @param + * @param readers * @param file the target file supposedly containing data for an * {@code InterpolationDataset}. * @return an {@code InterpolationDataset} extracted from { * @file} using the first available {@code DatasetReader} from the list - * @throws IOException if the reader has been found, but an error occurred - * when reading the file * @throws IllegalArgumentException if the file has an unsupported extension */ public static T read(List> readers, File file) { diff --git a/src/main/java/pulse/math/FixedIntervalIntegrator.java b/src/main/java/pulse/math/FixedIntervalIntegrator.java index 8a85798..ac77ccb 100644 --- a/src/main/java/pulse/math/FixedIntervalIntegrator.java +++ b/src/main/java/pulse/math/FixedIntervalIntegrator.java @@ -5,14 +5,10 @@ import static pulse.properties.NumericProperty.requireType; import static pulse.properties.NumericPropertyKeyword.INTEGRATION_SEGMENTS; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.Set; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import pulse.properties.Property; /** * A fixed-interval integrator implements a numerical scheme in which the domain @@ -64,7 +60,7 @@ public NumericProperty getIntegrationSegments() { * @param integrationSegments a property of the {@code INTEGRATION_SEGMENTS} * type */ - public void setIntegrationSegments(NumericProperty integrationSegments) { + public final void setIntegrationSegments(NumericProperty integrationSegments) { requireType(integrationSegments, INTEGRATION_SEGMENTS); this.integrationSegments = (int) integrationSegments.getValue(); } @@ -76,7 +72,7 @@ public void setIntegrationSegments(NumericProperty integrationSegments) { * @param bounds the integration bounds */ @Override - public void setBounds(Segment bounds) { + public final void setBounds(Segment bounds) { super.setBounds(bounds); } @@ -104,7 +100,7 @@ public Set listedKeywords() { * * @return the integration step size. */ - public double stepSize() { + public final double stepSize() { return getBounds().length() / (double) this.integrationSegments; } diff --git a/src/main/java/pulse/math/ParameterVector.java b/src/main/java/pulse/math/ParameterVector.java index 295f5a3..4b85221 100644 --- a/src/main/java/pulse/math/ParameterVector.java +++ b/src/main/java/pulse/math/ParameterVector.java @@ -7,7 +7,6 @@ import pulse.math.linear.Vector; import pulse.math.transforms.Transformable; import pulse.properties.NumericProperties; -import static pulse.properties.NumericProperties.def; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; diff --git a/src/main/java/pulse/math/transforms/InvLenSqTransform.java b/src/main/java/pulse/math/transforms/InvLenSqTransform.java index 2a84913..8b51287 100644 --- a/src/main/java/pulse/math/transforms/InvLenSqTransform.java +++ b/src/main/java/pulse/math/transforms/InvLenSqTransform.java @@ -16,12 +16,12 @@ public InvLenSqTransform(ThermalProperties tp) { @Override public double transform(double value) { - return value / (l * l); + return Math.abs(value) / (l * l); } @Override public double inverse(double t) { - return t * (l * l); + return Math.abs(t) * (l * l); } } diff --git a/src/main/java/pulse/math/transforms/StickTransform.java b/src/main/java/pulse/math/transforms/StickTransform.java index d68c935..00239cb 100644 --- a/src/main/java/pulse/math/transforms/StickTransform.java +++ b/src/main/java/pulse/math/transforms/StickTransform.java @@ -40,6 +40,7 @@ public StickTransform(Segment bounds) { } /** + * @param a * @see pulse.math.MathUtils.atanh() * @see pulse.math.Segment.getBounds() */ diff --git a/src/main/java/pulse/problem/laser/DiscretePulse.java b/src/main/java/pulse/problem/laser/DiscretePulse.java index d29c073..b27e4eb 100644 --- a/src/main/java/pulse/problem/laser/DiscretePulse.java +++ b/src/main/java/pulse/problem/laser/DiscretePulse.java @@ -2,9 +2,13 @@ import java.util.Objects; import pulse.input.ExperimentalData; +import pulse.math.MidpointIntegrator; +import pulse.math.Segment; 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.PULSE_WIDTH; import pulse.tasks.SearchTask; /** @@ -16,10 +20,23 @@ */ public class DiscretePulse { - private Grid grid; - private Pulse pulse; - private double discretePulseWidth; - private double timeFactor; + private final Grid grid; + private final Pulse pulse; + + private double widthOnGrid; + private double timeConversionFactor; + private double invTotalEnergy; //normalisation factor + + /** + * This number shows how small the actual pulse may be compared to the + * half-time. If the pulse is shorter than + * tc/{@value WIDTH_TOLERANCE_FACTOR}, it will be replaced + * by a rectangular pulse with the width equal to + * tc/{@value WIDTH_TOLERANCE_FACTOR}. Here + * tc + * is the time factor defined in the {@code Problem} class. + */ + private final static int WIDTH_TOLERANCE_FACTOR = 1000; /** * This creates a one-dimensional discrete pulse on a {@code grid}. @@ -34,25 +51,33 @@ public class DiscretePulse { */ public DiscretePulse(Problem problem, Grid grid) { this.grid = grid; - timeFactor = problem.getProperties().timeFactor(); + timeConversionFactor = problem.getProperties().timeFactor(); this.pulse = problem.getPulse(); - recalculate(); + Object ancestor + = Objects.requireNonNull(problem.specificAncestor(SearchTask.class), + "Problem has not been assigned to a SearchTask"); - Object ancestor = - Objects.requireNonNull( problem.specificAncestor(SearchTask.class), - "Problem has not been assigned to a SearchTask"); + ExperimentalData data = ((SearchTask) ancestor).getExperimentalCurve(); + init(data); - ExperimentalData data = ((SearchTask)ancestor).getExperimentalCurve(); - - pulse.getPulseShape().init(data, this); pulse.addListener(e -> { - timeFactor = problem.getProperties().timeFactor(); - recalculate(); - pulse.getPulseShape().init(data, this); + timeConversionFactor = problem.getProperties().timeFactor(); + init(data); }); + + grid.addListener(e + -> init(data) + ); } + + private void init(ExperimentalData data) { + widthOnGrid = 0; + recalculate(); + pulse.getPulseShape().init(data, this); + invTotalEnergy = 1.0/totalEnergy(); + } /** * Uses the {@code PulseTemporalShape} of the {@code Pulse} object to @@ -62,7 +87,7 @@ public DiscretePulse(Problem problem, Grid grid) { * @return the laser power at the specified moment of {@code time} */ public double laserPowerAt(double time) { - return pulse.getPulseShape().evaluateAt(time); + return invTotalEnergy * pulse.getPulseShape().evaluateAt(time); } /** @@ -71,18 +96,66 @@ public double laserPowerAt(double time) { * * @see pulse.problem.schemes.Grid.gridTime(double,double) */ - public void recalculate() { - final double width = ((Number) pulse.getPulseWidth().getValue()).doubleValue(); - discretePulseWidth = Math.max(grid.gridTime(width, timeFactor), grid.getTimeStep()); + public final void recalculate() { + final double nominalWidth = ((Number) pulse.getPulseWidth().getValue()).doubleValue(); + final double resolvedWidth = timeConversionFactor / getWidthToleranceFactor(); + + final double EPS = 1E-10; + + /** + * 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) { + //change shape to rectangular + var shape = new RectangularPulse(); + pulse.setPulseShape(shape); + //change pulse width + setDiscreteWidth(resolvedWidth); + shape.init(null, this); + //adjust the pulse object to update the visualised pulse + } else if(nominalWidth > resolvedWidth + EPS) { + setDiscreteWidth(nominalWidth); + } + + } + + /** + * 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(); + + var integrator = new MidpointIntegrator(new Segment(0, widthOnGrid)) { + + @Override + public double integrand(double... vars) { + return pulseShape.evaluateAt(vars[0]); + } + + }; + + return integrator.integrate(); } /** - * Gets the discrete pulse width defined by {@code DiscretePulse}. + * 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 a double, representing the discrete pulse width. + * @return the dimensionless pulse width mapped to the grid. */ public double getDiscreteWidth() { - return discretePulseWidth; + return widthOnGrid; + } + + private void setDiscreteWidth(double width) { + widthOnGrid = grid.gridTime(width, timeConversionFactor); + grid.adjustTimeStep(this); } /** @@ -102,5 +175,36 @@ 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 + * @return the conversion factor + */ + + public double getConversionFactor() { + return timeConversionFactor; + } + + /** + * 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(); + } + + /** + * 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 96ad965..ab9fd0e 100644 --- a/src/main/java/pulse/problem/laser/DiscretePulse2D.java +++ b/src/main/java/pulse/problem/laser/DiscretePulse2D.java @@ -19,7 +19,14 @@ public class DiscretePulse2D extends DiscretePulse { private double discretePulseSpot; - private double coordFactor; + private double sampleRadius; + private double normFactor; + + /** + * This had to be decreased for the 2d pulses. + */ + + private final static int WIDTH_TOLERANCE_FACTOR = 200; /** * The constructor for {@code DiscretePulse2D}. @@ -35,12 +42,10 @@ public class DiscretePulse2D extends DiscretePulse { public DiscretePulse2D(ClassicalProblem2D problem, Grid2D grid) { super(problem, grid); var properties = (ExtendedThermalProperties) problem.getProperties(); - coordFactor = (double) properties.getSampleDiameter().getValue() / 2.0; - var pulse = (Pulse2D) problem.getPulse(); - discretePulseSpot = grid.gridRadialDistance((double) pulse.getSpotDiameter().getValue() / 2.0, coordFactor); - + calcPulseSpot(properties); + properties.addListener(e -> calcPulseSpot(properties) ); } - + /** * This calculates the dimensionless, discretised pulse function at a * dimensionless radial coordinate {@code coord}. @@ -48,7 +53,7 @@ public DiscretePulse2D(ClassicalProblem2D problem, Grid2D grid) { * It uses a Heaviside function to determine whether the {@code radialCoord} * lies within the {@code 0 <= radialCoord <= discretePulseSpot} interval. * It uses the {@code time} parameter to determine the discrete pulse - * function using {@code evaluateAt(time)}. + * function using {@code evaluateAt(time)}.

* * @param time the time for calculation * @param radialCoord - the radial coordinate [length dimension] @@ -56,25 +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) * (0.5 + 0.5 * signum(discretePulseSpot - radialCoord)); + 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. + * @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; + evalPulseSpot(); } /** - * Calls the superclass method, then 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) */ - @Override - public void recalculate() { - super.recalculate(); - final double radius = (double) ((Pulse2D) getPulse()).getSpotDiameter().getValue() / 2.0; - discretePulseSpot = ((Grid2D) getGrid()).gridRadialDistance(radius, coordFactor); + public final void evalPulseSpot() { + var pulse = (Pulse2D) getPulse(); + var grid2d = (Grid2D) getGrid(); + final double spotRadius = (double) pulse.getSpotDiameter().getValue() / 2.0; + discretePulseSpot = grid2d.gridRadialDistance(spotRadius, sampleRadius); + grid2d.adjustStepSize(this); + normFactor = sampleRadius * sampleRadius / spotRadius / spotRadius; } - public double getDiscretePulseSpot() { + 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 21c3974..379ab97 100644 --- a/src/main/java/pulse/problem/laser/ExponentiallyModifiedGaussian.java +++ b/src/main/java/pulse/problem/laser/ExponentiallyModifiedGaussian.java @@ -10,14 +10,10 @@ import static pulse.properties.NumericPropertyKeyword.SKEW_MU; import static pulse.properties.NumericPropertyKeyword.SKEW_SIGMA; -import java.util.List; import java.util.Set; -import pulse.input.ExperimentalData; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.INTEGRATION_SEGMENTS; -import pulse.properties.Property; /** * Represents the exponentially modified Gaussian function, which is given by @@ -31,7 +27,8 @@ public class ExponentiallyModifiedGaussian extends PulseTemporalShape { private double mu; private double sigma; private double lambda; - private double norm; + + private final static int MIN_POINTS = 10; /** * Creates an exponentially modified Gaussian with the default parameter @@ -41,7 +38,6 @@ public ExponentiallyModifiedGaussian() { mu = (double) def(SKEW_MU).getValue(); lambda = (double) def(SKEW_LAMBDA).getValue(); sigma = (double) def(SKEW_SIGMA).getValue(); - norm = 1.0; } public ExponentiallyModifiedGaussian(ExponentiallyModifiedGaussian another) { @@ -49,18 +45,6 @@ public ExponentiallyModifiedGaussian(ExponentiallyModifiedGaussian another) { this.mu = another.mu; this.sigma = another.sigma; this.lambda = another.lambda; - this.norm = another.norm; - } - - /** - * This calls the superclass {@code init method} and sets the normalisation - * factor to 1/∫Φ(Fo)dFo. - */ - @Override - public void init(ExperimentalData data, DiscretePulse pulse) { - super.init(data, pulse); - norm = 1.0 / area(); // calculates the area. The normalisation factor is then set to the inverse of - // the area. } /** @@ -77,7 +61,7 @@ public double evaluateAt(double time) { final double lambdaHalf = 0.5 * lambda; final double sigmaSq = sigma * sigma; - return norm * lambdaHalf * exp(lambdaHalf * (2.0 * mu + lambda * sigmaSq - 2.0 * reducedTime)) + return lambdaHalf * exp(lambdaHalf * (2.0 * mu + lambda * sigmaSq - 2.0 * reducedTime)) * erfc((mu + lambda * sigmaSq - reducedTime) / (sqrt(2) * sigma)); } @@ -170,4 +154,9 @@ public PulseTemporalShape copy() { return new ExponentiallyModifiedGaussian(this); } -} + @Override + 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 60989bf..265ac2a 100644 --- a/src/main/java/pulse/problem/laser/NumericPulse.java +++ b/src/main/java/pulse/problem/laser/NumericPulse.java @@ -25,7 +25,8 @@ public class NumericPulse extends PulseTemporalShape { private NumericPulseData pulseData; private UnivariateFunction interpolation; - private double adjustedPulseWidth; + + private final static int MIN_POINTS = 20; public NumericPulse() { //intentionally blank @@ -47,59 +48,47 @@ public NumericPulse(NumericPulse pulse) { * interpolates the input pulse using spline functions and normalises the * output. * + * @param data * @see normalise() * */ @Override public void init(ExperimentalData data, DiscretePulse pulse) { - pulseData = data.getMetadata().getPulseData(); - - //subtracts a horizontal baseline from the pulse data - var baseline = new FlatBaseline(); - baseline.fitNegative(pulseData); - - for(int i = 0, size = pulseData.getTimeSequence().size(); i < size; i++) - pulseData.setSignalAt(i, - pulseData.signalAt(i) - baseline.valueAt(pulseData.timeAt(i))); + //generate baseline-subtracted numeric data from ExperimentalData + baselineSubtractedFrom(data); + //notify host pulse object of a new pulse width var problem = ((SearchTask) data.getParent()).getCurrentCalculation().getProblem(); - setPulseWidth(problem); + setPulseWidthOf(problem); + //convert to dimensionless time and interpolate double timeFactor = problem.getProperties().timeFactor(); - - super.init(data, pulse); - doInterpolation(timeFactor); - - normalise(problem); } - + /** - * Checks that the area of the pulse curve is unity (within a small error - * margin). If this is {@code false}, re-scales the numeric data using - * {@code 1/area} as the scaling factor. - * - * @param problem defines the {@code timeFactor} needed for re-building the - * interpolation - * @see pulse.problem.laser.NumericPulseData.scale() + * 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. */ - public void normalise(Problem problem) { - - final double EPS = 1E-2; - double timeFactor = problem.getProperties().timeFactor(); - - for (double area = area(); Math.abs(area - 1.0) > EPS; area = area()) { - pulseData.scale(1.0 / area); - doInterpolation(timeFactor); - } - + + private void baselineSubtractedFrom(ExperimentalData data) { + pulseData = new NumericPulseData(data.getMetadata().getPulseData()); + + //subtracts a horizontal baseline from the pulse data + var baseline = new FlatBaseline(); + baseline.fitNegative(pulseData); + + for(int i = 0, size = pulseData.getTimeSequence().size(); i < size; i++) + pulseData.setSignalAt(i, + pulseData.signalAt(i) - baseline.valueAt(pulseData.timeAt(i))); } - private void setPulseWidth(Problem problem) { - var timeSequence = pulseData.getTimeSequence(); - double pulseWidth = timeSequence.get(timeSequence.size() - 1); + private void setPulseWidthOf(Problem problem) { + 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)); } @@ -107,13 +96,13 @@ private void setPulseWidth(Problem problem) { private void doInterpolation(double timeFactor) { var interpolator = new AkimaSplineInterpolator(); - var timeList = pulseData.getTimeSequence().stream().mapToDouble(d -> d / timeFactor).toArray(); - adjustedPulseWidth = timeList[timeList.length - 1]; - 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()); - } /** @@ -122,7 +111,7 @@ private void doInterpolation(double timeFactor) { */ @Override public double evaluateAt(double time) { - return time > adjustedPulseWidth ? 0.0 : interpolation.value(time); + return time > getPulseWidth() ? 0.0 : interpolation.value(time); } @Override @@ -144,10 +133,16 @@ public NumericPulseData getData() { public void setData(NumericPulseData pulseData) { this.pulseData = pulseData; + } public UnivariateFunction getInterpolation() { return interpolation; } + @Override + public int getRequiredDiscretisation() { + return MIN_POINTS; + } + } diff --git a/src/main/java/pulse/problem/laser/NumericPulseData.java b/src/main/java/pulse/problem/laser/NumericPulseData.java index 0a0493e..e23c023 100644 --- a/src/main/java/pulse/problem/laser/NumericPulseData.java +++ b/src/main/java/pulse/problem/laser/NumericPulseData.java @@ -10,7 +10,7 @@ */ public class NumericPulseData extends AbstractData { - private int externalID; + private final int externalID; /** * Stores {@code id} and calls super-constructor @@ -50,20 +50,9 @@ public void addPoint(double time, double power) { public int getExternalID() { return externalID; } - - /** - * Uniformly scales the values of the pulse power by {@code factor}. - * - * @param factor the scaling factor - */ - public void scale(double factor) { - - var power = this.getSignalData(); - - for (int i = 0, size = power.size(); i < size; i++) { - power.set(i, power.get(i) * factor); - } - + + public double pulseWidth() { + return super.timeLimit(); } -} +} \ 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 5701951..6c3814e 100644 --- a/src/main/java/pulse/problem/laser/PulseTemporalShape.java +++ b/src/main/java/pulse/problem/laser/PulseTemporalShape.java @@ -1,12 +1,7 @@ package pulse.problem.laser; -import static pulse.properties.NumericProperties.derive; -import static pulse.properties.NumericPropertyKeyword.INTEGRATION_SEGMENTS; import pulse.input.ExperimentalData; -import pulse.math.FixedIntervalIntegrator; -import pulse.math.MidpointIntegrator; -import pulse.math.Segment; import pulse.util.PropertyHolder; import pulse.util.Reflexive; @@ -21,49 +16,14 @@ public abstract class PulseTemporalShape extends PropertyHolder implements Refle private double width; - private final static int DEFAULT_POINTS = 256; - private FixedIntervalIntegrator integrator; - public PulseTemporalShape() { //intentionlly blank } public PulseTemporalShape(PulseTemporalShape another) { - this.integrator = another.integrator; - } - - /** - * Creates a new midpoint-integrator using the number of segments equal to - * {@value DEFAULT_POINTS}. The integrand function is specified by the - * {@code evaluateAt} method of this class. - * - * @see pulse.math.MidpointIntegrator - * @see evaluateAt() - */ - public void initAreaIntegrator() { - integrator = new MidpointIntegrator(new Segment(0.0, getPulseWidth()), - derive(INTEGRATION_SEGMENTS, DEFAULT_POINTS)) { - - @Override - public double integrand(double... vars) { - return evaluateAt(vars[0]); - } - - }; + this.width = another.width; } - - /** - * Uses numeric integration (midpoint rule) to calculate the area of the - * pulse shape corresponding to the selected parameters. The integration - * bounds are non-negative. - * - * @return the area - */ - public double area() { - integrator.setBounds(new Segment(0.0, getPulseWidth())); - return integrator.integrate(); - } - + /** * This evaluates the dimensionless, discretised pulse function on a * {@code grid} needed to evaluate the heat source in the difference scheme. @@ -78,11 +38,11 @@ public double area() { * Stores the pulse width from {@code pulse} and initialises area * integration. * + * @param data * @param pulse the discrete pulse containing the pulse width */ public void init(ExperimentalData data, DiscretePulse pulse) { width = pulse.getDiscreteWidth(); - this.initAreaIntegrator(); } public abstract PulseTemporalShape copy(); @@ -104,5 +64,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 39369a2..2ba6b9d 100644 --- a/src/main/java/pulse/problem/laser/RectangularPulse.java +++ b/src/main/java/pulse/problem/laser/RectangularPulse.java @@ -14,8 +14,11 @@ */ public class RectangularPulse extends PulseTemporalShape { + private final static int MIN_POINTS = 4; + /** * @param time the time measured from the start of the laser pulse. + * @return */ @Override public double evaluateAt(double time) { @@ -32,5 +35,10 @@ 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 82bb854..c305b81 100644 --- a/src/main/java/pulse/problem/laser/TrapezoidalPulse.java +++ b/src/main/java/pulse/problem/laser/TrapezoidalPulse.java @@ -6,14 +6,10 @@ import static pulse.properties.NumericPropertyKeyword.TRAPEZOIDAL_FALL_PERCENTAGE; import static pulse.properties.NumericPropertyKeyword.TRAPEZOIDAL_RISE_PERCENTAGE; -import java.util.List; import java.util.Set; -import pulse.input.ExperimentalData; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.INTEGRATION_SEGMENTS; -import pulse.properties.Property; /** * A trapezoidal pulse shape, which combines a rise segment, a constant-power @@ -26,6 +22,8 @@ public class TrapezoidalPulse extends PulseTemporalShape { private double fall; private double h; + private final static int MIN_POINTS = 6; + /** * Constructs a trapezoidal pulse using a default segmentation principle. * The reader is referred to the {@code .xml} file containing the default @@ -36,7 +34,7 @@ public class TrapezoidalPulse extends PulseTemporalShape { public TrapezoidalPulse() { rise = (int) def(TRAPEZOIDAL_RISE_PERCENTAGE).getValue() / 100.0; fall = (int) def(TRAPEZOIDAL_FALL_PERCENTAGE).getValue() / 100.0; - h = height(); + h = 1.0; } public TrapezoidalPulse(TrapezoidalPulse another) { @@ -44,16 +42,7 @@ public TrapezoidalPulse(TrapezoidalPulse another) { this.fall = another.fall; this.h = another.h; } - - /** - * Calculates the height of the trapez after calling the super-class method. - */ - @Override - public void init(ExperimentalData data, DiscretePulse pulse) { - super.init(data, pulse); - h = height(); - } - + /** * Calculates the height of the trapezium which under current segmentation * will yield an area of unity. @@ -136,5 +125,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 2ff641f..db0c7e4 100644 --- a/src/main/java/pulse/problem/schemes/ADIScheme.java +++ b/src/main/java/pulse/problem/schemes/ADIScheme.java @@ -56,5 +56,16 @@ 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 + * @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 0c108ab..acaa5cd 100644 --- a/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java +++ b/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java @@ -11,15 +11,16 @@ */ public class BlockMatrixAlgorithm extends TridiagonalMatrixAlgorithm { - private double[] gamma; - private double[] p; - private double[] q; + private final double[] gamma; + private final double[] p; + private final double[] q; public BlockMatrixAlgorithm(Grid grid) { super(grid); - gamma = new double[getAlpha().length]; - p = new double[gamma.length - 1]; - q = new double[gamma.length - 1]; + final int N = this.getGridPoints(); + gamma = new double[N + 2]; + p = new double[N]; + q = new double[N]; } @Override @@ -33,9 +34,10 @@ public void sweep(double[] V) { @Override public void evaluateBeta(final double[] U) { super.evaluateBeta(U); - final int N = getGrid().getGridDensityValue(); var alpha = getAlpha(); var beta = getBeta(); + + final int N = getGridPoints(); p[N - 1] = beta[N]; q[N - 1] = alpha[N] + gamma[N]; @@ -49,8 +51,9 @@ public void evaluateBeta(final double[] U) { @Override public void evaluateBeta(final double[] U, final int start, final int endExclusive) { var alpha = getAlpha(); - var grid = getGrid(); - final double HX2_TAU = grid.getXStep() * grid.getXStep() / getGrid().getTimeStep(); + + final double h = this.getGridStep(); + final double HX2_TAU = h * h / this.getTimeStep(); final double a = getCoefA(); final double b = getCoefB(); diff --git a/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java b/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java index 848482b..879a51c 100644 --- a/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java +++ b/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java @@ -1,7 +1,5 @@ package pulse.problem.schemes; -import static pulse.properties.NumericProperties.def; -import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; import java.util.Set; @@ -13,18 +11,15 @@ import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -public abstract class CoupledImplicitScheme extends ImplicitScheme implements FixedPointIterations { +public abstract class CoupledImplicitScheme extends ImplicitScheme { private RadiativeTransferCoupling coupling; private RTECalculationStatus calculationStatus; - private double nonlinearPrecision; - - private double pls; + private boolean autoUpdateFluxes = true; //should be false for nonlinear solvers public CoupledImplicitScheme(NumericProperty N, NumericProperty timeFactor) { super(); setGrid(new Grid(N, timeFactor)); - nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); setCoupling(new RadiativeTransferCoupling()); calculationStatus = RTECalculationStatus.NORMAL; } @@ -33,37 +28,14 @@ public CoupledImplicitScheme(NumericProperty N, NumericProperty timeFactor, Nume this(N, timeFactor); setTimeLimit(timeLimit); } - - @Override - public void timeStep(final int m) throws SolverException { - pls = pulse(m); - doIterations(getCurrentSolution(), nonlinearPrecision, m); - } - - @Override - public void iteration(final int m) throws SolverException { - super.timeStep(m); - } - - @Override - public void finaliseIteration(double[] V) throws SolverException { - FixedPointIterations.super.finaliseIteration(V); - var rte = coupling.getRadiativeTransferEquation(); - setCalculationStatus(coupling.getRadiativeTransferEquation().compute(V)); - } - - public RadiativeTransferCoupling getCoupling() { - return coupling; - } - - public final void setCoupling(RadiativeTransferCoupling coupling) { - this.coupling = coupling; - this.coupling.setParent(this); - } - + @Override public void finaliseStep() throws SolverException { super.finaliseStep(); + if(autoUpdateFluxes) { + var rte = this.getCoupling().getRadiativeTransferEquation(); + setCalculationStatus(rte.compute(getCurrentSolution())); + } coupling.getRadiativeTransferEquation().getFluxes().store(); } @@ -74,45 +46,42 @@ public Set listedKeywords() { return set; } - public NumericProperty getNonlinearPrecision() { - return derive(NONLINEAR_PRECISION, nonlinearPrecision); - } - - public void setNonlinearPrecision(NumericProperty nonlinearPrecision) { - this.nonlinearPrecision = (double) nonlinearPrecision.getValue(); - } - - @Override - public Class domain() { - return ParticipatingMedium.class; - } - @Override public boolean normalOperation() { return super.normalOperation() && (getCalculationStatus() == RTECalculationStatus.NORMAL); } - @Override - public void set(NumericPropertyKeyword type, NumericProperty property) { - if (type == NONLINEAR_PRECISION) { - setNonlinearPrecision(property); - } else { - super.set(type, property); - } - } - - public RTECalculationStatus getCalculationStatus() { + public final RTECalculationStatus getCalculationStatus() { return calculationStatus; } - public void setCalculationStatus(RTECalculationStatus calculationStatus) throws SolverException { + public final void setCalculationStatus(RTECalculationStatus calculationStatus) throws SolverException { this.calculationStatus = calculationStatus; - if(calculationStatus != RTECalculationStatus.NORMAL) + if (calculationStatus != RTECalculationStatus.NORMAL) { throw new SolverException(calculationStatus.toString()); + } + } + + public final RadiativeTransferCoupling getCoupling() { + return coupling; } - public double getCurrentPulseValue() { - return pls; + 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 09ea1cf..64e33e1 100644 --- a/src/main/java/pulse/problem/schemes/DifferenceScheme.java +++ b/src/main/java/pulse/problem/schemes/DifferenceScheme.java @@ -12,7 +12,8 @@ import pulse.problem.statements.Problem; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.NUMPOINTS; +import static pulse.properties.NumericPropertyKeyword.GRID_DENSITY; +import static pulse.properties.NumericPropertyKeyword.TAU_FACTOR; import pulse.util.PropertyHolder; import pulse.util.Reflexive; @@ -33,6 +34,7 @@ public abstract class DifferenceScheme extends PropertyHolder implements Reflexi private Grid grid; private double timeLimit; + private double pls; private int timeInterval; private static boolean hideDetailedAdjustment = true; @@ -61,23 +63,6 @@ public void initFrom(DifferenceScheme another) { this.timeInterval = another.timeInterval; } - /** - * Used to get a class of problems on which this difference scheme is - * applicable. - * - * @return a subclass of the {@code Problem} class which can be used as - * input for this difference scheme. - */ - public abstract Class domain(); - - /** - * Creates a {@code DifferenceScheme}, which is an exact copy of this - * object. - * - * @return an exact copy of this {@code DifferenceScheme}. - */ - public abstract DifferenceScheme copy(); - /** * Copies the {@code Grid} and {@code timeLimit} from {@code df}. * @@ -91,91 +76,88 @@ public void copyFrom(DifferenceScheme df) { /** *

- * Contains preparatory steps to ensure smooth running of the solver. This - * includes creating a {@code DiscretePulse} object and adjusting the grid - * of this scheme to match the {@code DiscretePulse} created for this - * {@code problem}. Finally, a heating curve is cleared from the previously - * calculated values. - *

+ * Contains preparatory steps to ensure smooth running of the solver.This + * includes creating a {@code DiscretePulse}object and adjusting the grid of + * this scheme to match the {@code DiscretePulse}created for this + * {@code problem} Finally, a heating curve is cleared from the previously + * calculated values.

*

* All subclasses of {@code DifferenceScheme} should override and explicitly * call this superclass method where appropriate. *

* * @param problem the heat problem to be solved + * @throws pulse.problem.schemes.solvers.SolverException * @see pulse.problem.schemes.Grid.adjustTo() */ - protected void prepare(Problem problem) { - if(discretePulse == null) + protected void prepare(Problem problem) throws SolverException { + if (discretePulse == null) { discretePulse = problem.discretePulseOn(grid); - else + } + else { discretePulse.recalculate(); + } - grid.adjustTo(discretePulse); - - var hc = problem.getHeatingCurve(); - hc.clear(); + clearArrays(); } public void runTimeSequence(Problem problem) throws SolverException { runTimeSequence(problem, 0, timeLimit); + } + + public void scaleSolution(Problem problem) { var curve = problem.getHeatingCurve(); final double maxTemp = (double) problem.getProperties().getMaximumTemperature().getValue(); - curve.scale(maxTemp / curve.apparentMaximum()); + //curve.scale(maxTemp / curve.apparentMaximum()); + curve.scale(maxTemp); } public void runTimeSequence(Problem problem, final double offset, final double endTime) throws SolverException { var curve = problem.getHeatingCurve(); + curve.clear(); - int adjustedNumPoints = (int) curve.getNumPoints().getValue(); + int numPoints = (int) curve.getNumPoints().getValue(); - final double startTime = (double) curve.getTimeShift().getValue(); + final double startTime = (double) curve.getTimeShift().getValue(); final double timeSegment = (endTime - startTime - offset) / problem.getProperties().timeFactor(); - final double tau = grid.getTimeStep(); - for (double dt = 0, factor; dt < tau; adjustedNumPoints *= factor) { - dt = timeSegment / (adjustedNumPoints - 1); - factor = dt / tau; - timeInterval = (int) factor; - } + double tau = grid.getTimeStep(); + final double dt = timeSegment / (numPoints - 1); + timeInterval = Math.max( (int) (dt / tau), 1); - final double wFactor = timeInterval * tau * problem.getProperties().timeFactor(); + double wFactor = timeInterval * tau * problem.getProperties().timeFactor(); // First point (index = 0) is always (0.0, 0.0) + curve.addPoint(0.0, 0.0); + + double nextTime; + int previous; /* - * The outer cycle iterates over the number of points of the HeatingCurve + * The outer cycle iterates over the number of points of the HeatingCurve */ - double nextTime = offset + wFactor; - curve.addPoint(0.0, 0.0); - - for (int w = 1; nextTime < 1.01 * endTime; nextTime = offset + (++w) * wFactor) { + for (previous = 1, nextTime = offset; nextTime < endTime || !curve.isFull(); + previous += timeInterval) { /* - * Two adjacent points of the heating curves are separated by timeInterval on - * the time grid. Thus, to calculate the next point on the heating curve, - * timeInterval/tau time steps have to be made first. + * Two adjacent points of the heating curves are separated by timeInterval on + * the time grid. Thus, to calculate the next point on the heating curve, + * timeInterval/tau time steps have to be made first. */ - timeSegment((w - 1) * timeInterval + 1, w * timeInterval + 1); + timeSegment(previous, previous + timeInterval); + nextTime += wFactor; curve.addPoint(nextTime, signal()); - - } - - /** - * If the total number of points added by the procedure - * is actually less than the pre-set number of points -- change that number - */ - - if(curve.actualNumPoints() < (int)curve.getNumPoints().getValue()) { - curve.setNumPoints(derive(NUMPOINTS, curve.actualNumPoints())); } + curve.copyToLastCalculation(); + scaleSolution(problem); } private void timeSegment(final int m1, final int m2) throws SolverException { for (int m = m1; m < m2 && normalOperation(); m++) { - timeStep(m); - finaliseStep(); + prepareStep(m); //prepare + timeStep(m); //calculate + finaliseStep(); //finalise } } @@ -183,11 +165,15 @@ public double pulse(final int m) { return getDiscretePulse().laserPowerAt((m - EPS) * getGrid().getTimeStep()); } - public abstract double signal(); - - public abstract void timeStep(final int m) throws SolverException; - - public abstract void finaliseStep() throws SolverException; + /** + * Do preparatory calculations that depend only on the time variable, e.g., + * calculate the pulse power. + * + * @param m the time step number + */ + public void prepareStep(int m) { + pls = pulse(m); + } public boolean normalOperation() { return true; @@ -290,6 +276,10 @@ public final NumericProperty getTimeLimit() { return derive(TIME_LIMIT, timeLimit); } + public double getCurrentPulseValue() { + return pls; + } + /** * Sets the time limit (in units defined by the corresponding * {@code NumericProperty}), which serves as the breakpoint for the @@ -311,5 +301,30 @@ 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. + * + * @return an array containing subclasses of the {@code Problem} class which + * can be used as input for this difference scheme. + */ + public abstract Class[] domain(); + + /** + * Creates a {@code DifferenceScheme}, which is an exact copy of this + * object. + * + * @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 2ef708d..88831e4 100644 --- a/src/main/java/pulse/problem/schemes/DistributedDetection.java +++ b/src/main/java/pulse/problem/schemes/DistributedDetection.java @@ -35,4 +35,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/Grid.java b/src/main/java/pulse/problem/schemes/Grid.java index d62e7dc..f68467b 100644 --- a/src/main/java/pulse/problem/schemes/Grid.java +++ b/src/main/java/pulse/problem/schemes/Grid.java @@ -3,21 +3,16 @@ import static java.lang.Math.pow; import static java.lang.Math.rint; import static java.lang.String.format; -import static pulse.properties.NumericProperties.def; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericProperty.requireType; import static pulse.properties.NumericPropertyKeyword.GRID_DENSITY; import static pulse.properties.NumericPropertyKeyword.TAU_FACTOR; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import pulse.problem.laser.DiscretePulse; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; -import pulse.properties.Property; import pulse.util.PropertyHolder; /** @@ -48,8 +43,10 @@ public class Grid extends PropertyHolder { * @see pulse.properties.NumericPropertyKeyword */ public Grid(NumericProperty gridDensity, NumericProperty timeFactor) { - setGridDensity(gridDensity); - setTimeFactor(timeFactor); + this.N = (int) gridDensity.getValue(); + this.tauFactor = (double) timeFactor.getValue(); + hx = 1. / N; + setTimeStep(tauFactor * pow(hx, 2)); } protected Grid() { @@ -67,20 +64,34 @@ public Grid copy() { } /** - * Optimises the {@code Grid} parameters. + * 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 < grid.tau}. + * {@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 void adjustTo(DiscretePulse pulse) { - final double ADJUSTMENT_FACTOR = 0.75; - for (final double factor = 0.95; factor * tau > pulse.getDiscreteWidth(); pulse.recalculate()) { - tauFactor *= ADJUSTMENT_FACTOR; - tau = tauFactor * pow(hx, 2); + 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)); } + } /** @@ -115,7 +126,7 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { * * @return a double, representing the {@code hx} value. */ - public double getXStep() { + public final double getXStep() { return hx; } @@ -124,7 +135,7 @@ public double getXStep() { * * @param hx a double, representing the new {@code hx} value. */ - public void setXStep(double hx) { + public final void setXStep(double hx) { this.hx = hx; } @@ -134,12 +145,13 @@ public void setXStep(double hx) { * * @return a double, representing the {@code tau} value. */ - public double getTimeStep() { + public final double getTimeStep() { return tau; } - protected void setTimeStep(double tau) { + protected final void setTimeStep(double tau) { this.tau = tau; + } /** @@ -150,7 +162,7 @@ protected void setTimeStep(double tau) { * @return a NumericProperty of the {@code TAU_FACTOR} type, representing * the {@code tauFactor} value. */ - public NumericProperty getTimeFactor() { + public final NumericProperty getTimeFactor() { return derive(TAU_FACTOR, tauFactor); } @@ -161,16 +173,17 @@ public NumericProperty getTimeFactor() { * @return a NumericProperty of the {@code GRID_DENSITY} type, representing * the {@code gridDensity} value. */ - public NumericProperty getGridDensity() { + public final NumericProperty getGridDensity() { return derive(GRID_DENSITY, N); } - protected int getGridDensityValue() { + protected final int getGridDensityValue() { return N; } protected void setGridDensityValue(int N) { this.N = N; + hx = 1. / N; } /** @@ -183,7 +196,8 @@ public void setGridDensity(NumericProperty gridDensity) { requireType(gridDensity, GRID_DENSITY); this.N = (int) gridDensity.getValue(); hx = 1. / N; - setTimeFactor(derive(TAU_FACTOR, 1.0)); + setTimeStep(tauFactor * pow(hx, 2)); + firePropertyChanged(this, gridDensity); } /** @@ -195,7 +209,8 @@ 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); } /** @@ -207,7 +222,7 @@ public void setTimeFactor(NumericProperty timeFactor) { * @param dimensionFactor a conversion factor with the dimension of time * @return a double representing the time on the finite grid */ - public double gridTime(double time, double dimensionFactor) { + public final double gridTime(double time, double dimensionFactor) { return rint((time / dimensionFactor) / tau) * tau; } @@ -220,16 +235,21 @@ public double gridTime(double time, double dimensionFactor) { * @param lengthFactor a conversion factor with the dimension of length * @return a double representing the axial distance on the finite grid */ - public double gridAxialDistance(double distance, double lengthFactor) { + public final double gridAxialDistance(double distance, double lengthFactor) { return rint((distance / lengthFactor) / hx) * hx; } @Override public String toString() { var sb = new StringBuilder(); - sb.append(""); - sb.append(getClass().getSimpleName() + ": hx=" + format("%3.2e", hx) + "; "); - sb.append("τ=" + format("%3.2e", tau) + "; "); + sb.append(""). + append(getClass().getSimpleName()) + .append(": hx=") + .append(format("%3.2e", hx)) + .append("; "). + append("τ=") + .append(format("%3.2e", tau)) + .append("; "); return sb.toString(); } diff --git a/src/main/java/pulse/problem/schemes/Grid2D.java b/src/main/java/pulse/problem/schemes/Grid2D.java index e70710a..7c34840 100644 --- a/src/main/java/pulse/problem/schemes/Grid2D.java +++ b/src/main/java/pulse/problem/schemes/Grid2D.java @@ -56,24 +56,25 @@ public void setTimeFactor(NumericProperty timeFactor) { * * @param pulse the discrete puls representation */ - @Override - public void adjustTo(DiscretePulse pulse) { - super.adjustTo(pulse); - if (pulse instanceof DiscretePulse2D) { - adjustTo((DiscretePulse2D) pulse); + + public void adjustStepSize(DiscretePulse pulse) { + var pulse2d = (DiscretePulse2D)pulse; + double pulseSpotSize = pulse2d.getDiscretePulseSpot(); + + if(hy > pulseSpotSize) { + final int INCREMENT = 5; + final int newN = getGridDensityValue() + INCREMENT; + setGridDensityValue(newN); + adjustStepSize(pulse); } + + adjustTimeStep(pulse); } - private void adjustTo(DiscretePulse2D pulse) { - final int GRID_DENSITY_INCREMENT = 5; - - for (final var factor = 1.05; factor * hy > pulse.getDiscretePulseSpot(); pulse.recalculate()) { - int N = getGridDensityValue(); - setGridDensityValue(N + GRID_DENSITY_INCREMENT); - hy = 1. / N; - setXStep(1. / N); - } - + @Override + protected void setGridDensityValue(int N) { + super.setGridDensityValue(N); + hy = 1. / N; } /** @@ -101,13 +102,11 @@ public double gridRadialDistance(double radial, double lengthFactor) { @Override public String toString() { - var sb = new StringBuilder(super.toString()); - sb.append("hy=" + format("%3.3f", hy)); - return sb.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 ae80435..8d85284 100644 --- a/src/main/java/pulse/problem/schemes/ImplicitScheme.java +++ b/src/main/java/pulse/problem/schemes/ImplicitScheme.java @@ -61,7 +61,7 @@ public ImplicitScheme(NumericProperty N, NumericProperty timeFactor, NumericProp } @Override - protected void prepare(Problem problem) { + protected void prepare(Problem problem) throws SolverException { super.prepare(problem); tridiagonal = new TridiagonalMatrixAlgorithm(getGrid()); } @@ -77,21 +77,21 @@ protected void prepare(Problem problem) { @Override public void timeStep(final int m) throws SolverException { - leftBoundary(m); + leftBoundary(); final var V = getCurrentSolution(); final int N = V.length - 1; - setSolutionAt(N, evalRightBoundary(m, tridiagonal.getAlpha()[N], tridiagonal.getBeta()[N])); + setSolutionAt(N, evalRightBoundary(tridiagonal.getAlpha()[N], tridiagonal.getBeta()[N])); tridiagonal.sweep(V); } - public void leftBoundary(final int m) { - tridiagonal.setBeta(1, firstBeta(m)); + public void leftBoundary() { + tridiagonal.setBeta(1, firstBeta()); tridiagonal.evaluateBeta(getPreviousSolution()); } - public abstract double evalRightBoundary(final int m, final double alphaN, final double betaN); + public abstract double evalRightBoundary(final double alphaN, final double betaN); - public abstract double firstBeta(final int m); + public abstract double firstBeta(); /** * Prints out the description of this problem type. @@ -111,4 +111,4 @@ public void setTridiagonalMatrixAlgorithm(TridiagonalMatrixAlgorithm tridiagonal this.tridiagonal = tridiagonal; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/problem/schemes/LayeredGrid2D.java b/src/main/java/pulse/problem/schemes/LayeredGrid2D.java deleted file mode 100644 index 0cea4c9..0000000 --- a/src/main/java/pulse/problem/schemes/LayeredGrid2D.java +++ /dev/null @@ -1,85 +0,0 @@ -package pulse.problem.schemes; - -import static pulse.problem.schemes.Partition.Location.CORE_X; -import static pulse.problem.schemes.Partition.Location.CORE_Y; -import static pulse.problem.schemes.Partition.Location.FRONT_Y; -import static pulse.problem.schemes.Partition.Location.REAR_Y; -import static pulse.problem.schemes.Partition.Location.SIDE_X; -import static pulse.problem.schemes.Partition.Location.SIDE_Y; -import static pulse.properties.NumericProperties.def; -import static pulse.properties.NumericProperties.derive; -import static pulse.properties.NumericPropertyKeyword.SHELL_GRID_DENSITY; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import pulse.problem.schemes.Partition.Location; -import pulse.properties.NumericProperty; -import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; -import pulse.properties.Property; - -public class LayeredGrid2D extends Grid2D { - - private Map h; - - public LayeredGrid2D(Map partitions, NumericProperty timeFactor) { - h = new HashMap<>(partitions); - setGridDensity(derive(CORE_Y.densityKeyword(), partitions.get(CORE_Y).getDensity())); - setTimeFactor(timeFactor); - } - - public Partition getPartition(Location location) { - return h.get(location); - } - - @Override - public Grid2D copy() { - return new LayeredGrid2D(h, getTimeFactor()); - } - - private void setDensity(Location location, NumericProperty density) { - h.get(location).setDensity((int) density.getValue()); - } - - @Override - public void setGridDensity(NumericProperty gridDensity) { - super.setGridDensity(gridDensity); - setDensity(CORE_X, gridDensity); - setDensity(CORE_Y, gridDensity); - } - - public NumericProperty getGridDensity(Location location) { - return derive(location.densityKeyword(), h.get(location).getDensity()); - } - - @Override - public NumericProperty getGridDensity() { - return getGridDensity(CORE_X); - } - - @Override - public Set listedKeywords() { - var set = super.listedKeywords(); - set.add(SHELL_GRID_DENSITY); - return set; - } - - @Override - public void set(NumericPropertyKeyword type, NumericProperty property) { - switch (type) { - case SHELL_GRID_DENSITY: - setDensity(FRONT_Y, property); - setDensity(REAR_Y, property); - setDensity(SIDE_X, property); - setDensity(SIDE_Y, property); - break; - default: - super.set(type, property); - } - } - -} diff --git a/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java b/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java index 54b3395..5483b5a 100644 --- a/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java +++ b/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java @@ -1,7 +1,6 @@ package pulse.problem.schemes; import pulse.problem.schemes.solvers.SolverException; -import pulse.problem.statements.Problem; import pulse.properties.NumericProperty; public abstract class OneDimensionalScheme extends DifferenceScheme { @@ -16,10 +15,10 @@ protected OneDimensionalScheme() { protected OneDimensionalScheme(NumericProperty timeLimit) { super(timeLimit); } - + + @Override - protected void prepare(Problem problem) { - super.prepare(problem); + public void clearArrays() { final int N = (int) getGrid().getGridDensity().getValue(); U = new double[N + 1]; V = new double[N + 1]; diff --git a/src/main/java/pulse/problem/schemes/Partition.java b/src/main/java/pulse/problem/schemes/Partition.java deleted file mode 100644 index 563fb9f..0000000 --- a/src/main/java/pulse/problem/schemes/Partition.java +++ /dev/null @@ -1,66 +0,0 @@ -package pulse.problem.schemes; - -import pulse.properties.NumericPropertyKeyword; - -public class Partition { - - private int density; - private double multiplier; - private double shift; - - public Partition(int value, double multiplier, double shift) { - this.setDensity(value); - this.setShift(shift); - this.setGridMultiplier(multiplier); - } - - public double evaluate() { - return multiplier / (density * (1.0 + shift)); - } - - public int getDensity() { - return density; - } - - public void setDensity(int density) { - this.density = density; - } - - public double getGridMultiplier() { - return multiplier; - } - - public void setGridMultiplier(double multiplier) { - this.multiplier = multiplier; - } - - public double getShift() { - return shift; - } - - public void setShift(double shift) { - this.shift = shift; - } - - public enum Location { - - FRONT_Y, REAR_Y, SIDE_Y, SIDE_X, CORE_X, CORE_Y; - - public NumericPropertyKeyword densityKeyword() { - switch (this) { - case FRONT_Y: - case REAR_Y: - case SIDE_Y: - case SIDE_X: - return NumericPropertyKeyword.SHELL_GRID_DENSITY; - case CORE_X: - case CORE_Y: - return NumericPropertyKeyword.GRID_DENSITY; - default: - throw new IllegalArgumentException("Type not recognized: " + this); - } - } - - } - -} diff --git a/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java b/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java index 73801b7..189548a 100644 --- a/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java +++ b/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java @@ -4,7 +4,10 @@ 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; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; import pulse.properties.Property; @@ -31,19 +34,24 @@ 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); + } + newRTE(problem, grid); instanceDescriptor.addListener(() -> { newRTE(problem, grid); rte.init(problem, grid); }); - + } else { rte.init(problem, grid); } } - private void newRTE(ParticipatingMedium problem, Grid grid) { + private void newRTE(Problem problem, Grid grid) { rte = instanceDescriptor.newInstance(RadiativeTransferSolver.class, problem, grid); rte.setParent(this); } diff --git a/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java b/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java index 7426317..88c7c65 100644 --- a/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java +++ b/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java @@ -8,20 +8,23 @@ */ public class TridiagonalMatrixAlgorithm { - private Grid grid; + private final double tau; + private final double h; private double a; private double b; private double c; - private double[] alpha; - private double[] beta; + private final int N; + private final double[] alpha; + private final double[] beta; public TridiagonalMatrixAlgorithm(Grid grid) { - this.grid = grid; - final int N = grid.getGridDensityValue(); - alpha = new double[N + 2]; - beta = new double[N + 2]; + tau = grid.getTimeStep(); + N = grid.getGridDensityValue(); + h = grid.getXStep(); + alpha = new double[N + 2]; + beta = new double[N + 2]; } /** @@ -34,7 +37,7 @@ public TridiagonalMatrixAlgorithm(Grid grid) { * from the respective boundary condition */ public void sweep(double[] V) { - for (int j = grid.getGridDensityValue() - 1; j >= 0; j--) { + for (int j = N - 1; j >= 0; j--) { V[j] = alpha[j + 1] * V[j + 1] + beta[j + 1]; } } @@ -44,21 +47,23 @@ public void sweep(double[] V) { * matrix algorithm. */ public void evaluateAlpha() { - for (int i = 1, N = grid.getGridDensityValue(); i < N; i++) { + for (int i = 1; i < N; i++) { alpha[i + 1] = c / (b - a * alpha[i]); } } public void evaluateBeta(final double[] U) { - evaluateBeta(U, 2, grid.getGridDensityValue() + 1); + evaluateBeta(U, 2, N + 1); } /** * Calculates the {@code beta} coefficients as part of the tridiagonal * matrix algorithm. + * @param U + * @param start + * @param endExclusive */ public void evaluateBeta(final double[] U, final int start, final int endExclusive) { - final double tau = grid.getTimeStep(); for (int i = start; i < endExclusive; i++) { beta[i] = beta(U[i - 1] / tau, phi(i - 1), i); } @@ -111,9 +116,17 @@ protected double getCoefB() { protected double getCoefC() { return c; } - - public Grid getGrid() { - return grid; + + 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 a0e7880..f395d00 100644 --- a/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java +++ b/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java @@ -1,12 +1,12 @@ package pulse.problem.schemes.rte; -import static pulse.math.MathUtils.fastPowLoop; import org.apache.commons.math3.analysis.UnivariateFunction; - +import static pulse.math.MathUtils.fastPowLoop; import pulse.problem.statements.NonlinearProblem; import pulse.problem.statements.Pulse2D; + /** * Contains methods for calculating the integral spectral characteristics of a * black body with a specific spatial temperature profile. The latter is managed @@ -16,8 +16,8 @@ */ public class BlackbodySpectrum { - private double reductionFactor; private UnivariateFunction interpolation; + private final double reductionFactor; /** * Creates a {@code BlackbodySpectrum}. Calculates the reduction factor @@ -32,6 +32,24 @@ public BlackbodySpectrum(NonlinearProblem p) { reductionFactor = maxHeating / ((double) p.getProperties().getTestTemperature().getValue()); } + @Override + 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); + } + /** * Calculates the spectral radiance, which is equal to the spectral power * divided by π, at the given coordinate. @@ -45,10 +63,7 @@ public double radianceAt(double x) { } /** - * Calculates the emissive power at the given coordinate. This is equal to - * 0.25 T0Tm [1 - * +δTm /T0 θ (x) - * ]4, where θ is the reduced temperature. + * Calculates the emissive power at the given coordinate. * * @param x the geometric coordinate inside the sample * @return the local emissive power value @@ -71,17 +86,8 @@ public UnivariateFunction getInterpolation() { return interpolation; } - @Override - public String toString() { - return "[" + getClass().getSimpleName() + ": Rel. heating = " + reductionFactor + "]"; - } - - private double emissivePower(double reducedTemperature) { - return 0.25 / reductionFactor * fastPowLoop(1.0 + reducedTemperature * reductionFactor, 4); - } - - private double radiance(double reducedTemperature) { + public final double radiance(double reducedTemperature) { return emissivePower(reducedTemperature) / Math.PI; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/problem/schemes/rte/RadiativeTransferSolver.java b/src/main/java/pulse/problem/schemes/rte/RadiativeTransferSolver.java index bb23d09..8013c33 100644 --- a/src/main/java/pulse/problem/schemes/rte/RadiativeTransferSolver.java +++ b/src/main/java/pulse/problem/schemes/rte/RadiativeTransferSolver.java @@ -27,7 +27,7 @@ public abstract class RadiativeTransferSolver extends PropertyHolder implements Reflexive, Descriptive { private Fluxes fluxes; - private List rteListeners; + private final List rteListeners; /** * Dummy constructor. @@ -47,17 +47,17 @@ public RadiativeTransferSolver() { /** * Retrieves the parameters from {@code p} and {@code grid} needed to run - * the calculations. Resets the flux arrays. + * the calculations.Resets the flux arrays. * - * @param p the problem statement + * @param p * @param grid the grid */ public void init(ParticipatingMedium p, Grid grid) { if (fluxes != null) { fluxes.setDensity(grid.getGridDensity()); fluxes.init(); - var properties = (ThermoOpticalProperties) p.getProperties(); - fluxes.setOpticalThickness(properties.getOpticalThickness()); + ThermoOpticalProperties top = (ThermoOpticalProperties) p.getProperties(); + fluxes.setOpticalThickness(top.getOpticalThickness()); } } @@ -128,11 +128,11 @@ public void fireStatusUpdate(RTECalculationStatus status) { } } - public Fluxes getFluxes() { + public final Fluxes getFluxes() { return fluxes; } - public void setFluxes(Fluxes fluxes) { + public final void setFluxes(Fluxes fluxes) { this.fluxes = fluxes; } diff --git a/src/main/java/pulse/problem/schemes/rte/dom/CornetteSchanksPF.java b/src/main/java/pulse/problem/schemes/rte/dom/CornetteSchanksPF.java new file mode 100644 index 0000000..f2a695e --- /dev/null +++ b/src/main/java/pulse/problem/schemes/rte/dom/CornetteSchanksPF.java @@ -0,0 +1,42 @@ +package pulse.problem.schemes.rte.dom; + +import static java.lang.Math.sqrt; + +import pulse.problem.statements.ParticipatingMedium; +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 + * @see https://doi.org/10.1364/ao.31.003152 + * + */ +public class CornetteSchanksPF extends PhaseFunction { + + private double anisoFactor; + private double onePlusGSq; + private double g2; + + public CornetteSchanksPF(ThermoOpticalProperties top, Discretisation intensities) { + super(top, intensities); + } + + @Override + public void init(ThermoOpticalProperties top) { + super.init(top); + final double anisotropy = getAnisotropyFactor(); + g2 = 2.0 * anisotropy; + final double aSq = anisotropy * anisotropy; + onePlusGSq = 1.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); + final double f = onePlusGSq - g2 * cosine; + 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 5ad9d0d..af59449 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/DiscreteOrdinatesMethod.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/DiscreteOrdinatesMethod.java @@ -6,6 +6,7 @@ import pulse.problem.schemes.rte.FluxesAndExplicitDerivatives; import pulse.problem.schemes.rte.RTECalculationStatus; import pulse.problem.schemes.rte.RadiativeTransferSolver; +import pulse.problem.statements.ClassicalProblem; import pulse.problem.statements.ParticipatingMedium; import pulse.problem.statements.model.ThermoOpticalProperties; import pulse.properties.NumericProperty; @@ -45,7 +46,7 @@ public DiscreteOrdinatesMethod(ParticipatingMedium problem, Grid grid) { var properties = (ThermoOpticalProperties) problem.getProperties(); setFluxes(new FluxesAndExplicitDerivatives(grid.getGridDensity(), properties.getOpticalThickness())); - var discrete = new Discretisation(problem); + var discrete = new Discretisation(properties); integratorDescriptor.setSelectedDescriptor(TRBDF2.class.getSimpleName()); setIntegrator(integratorDescriptor.newInstance(AdaptiveIntegrator.class, discrete)); @@ -54,8 +55,8 @@ public DiscreteOrdinatesMethod(ParticipatingMedium problem, Grid grid) { setIterativeSolver(iterativeSolverSelector.newInstance(IterativeSolver.class)); phaseFunctionSelector.setSelectedDescriptor(HenyeyGreensteinPF.class.getSimpleName()); - phaseFunctionSelector.addListener(() -> initPhaseFunction(problem, discrete)); - initPhaseFunction(problem, discrete); + phaseFunctionSelector.addListener(() -> initPhaseFunction(properties, discrete)); + initPhaseFunction(properties, discrete); init(problem, grid); @@ -83,7 +84,8 @@ public RTECalculationStatus compute(double[] tempArray) { } private void fluxesAndDerivatives(final int nExclusive) { - final var interpolation = integrator.getHermiteInterpolator().interpolateOnExternalGrid(nExclusive, integrator); + final var interpolation = integrator.getHermiteInterpolator() + .interpolateOnExternalGrid(nExclusive, integrator); final double DOUBLE_PI = 2.0 * Math.PI; final var discrete = integrator.getDiscretisation(); @@ -92,7 +94,8 @@ 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, -DOUBLE_PI * discrete.firstMoment(interpolation[1], i)); + fluxes.setFluxDerivative(i, + -DOUBLE_PI * discrete.firstMoment(interpolation[1], i)); } } @@ -105,9 +108,11 @@ public String getDescriptor() { @Override public void init(ParticipatingMedium problem, Grid grid) { super.init(problem, grid); - initPhaseFunction(problem, integrator.getDiscretisation()); + var top = (ThermoOpticalProperties)problem.getProperties(); + initPhaseFunction(top, + integrator.getDiscretisation()); integrator.init(problem); - integrator.getPhaseFunction().init(problem); + integrator.getPhaseFunction().init(top); } @Override @@ -119,35 +124,35 @@ public List listedTypes() { return list; } - public AdaptiveIntegrator getIntegrator() { + public final AdaptiveIntegrator getIntegrator() { return integrator; } - public InstanceDescriptor getIntegratorDescriptor() { + public final InstanceDescriptor getIntegratorDescriptor() { return integratorDescriptor; } - public void setIntegrator(AdaptiveIntegrator integrator) { + public final void setIntegrator(AdaptiveIntegrator integrator) { this.integrator = integrator; integrator.setParent(this); firePropertyChanged(this, integratorDescriptor); } - public IterativeSolver getIterativeSolver() { + public final IterativeSolver getIterativeSolver() { return iterativeSolver; } - public InstanceDescriptor getIterativeSolverSelector() { + public final InstanceDescriptor getIterativeSolverSelector() { return iterativeSolverSelector; } - public void setIterativeSolver(IterativeSolver solver) { + public final void setIterativeSolver(IterativeSolver solver) { this.iterativeSolver = solver; solver.setParent(this); firePropertyChanged(this, iterativeSolverSelector); } - public InstanceDescriptor getPhaseFunctionSelector() { + public final InstanceDescriptor getPhaseFunctionSelector() { return phaseFunctionSelector; } @@ -161,10 +166,10 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { // intentionally left blank } - private void initPhaseFunction(ParticipatingMedium problem, Discretisation discrete) { - var pf = phaseFunctionSelector.newInstance(PhaseFunction.class, problem, discrete); + private void initPhaseFunction(ThermoOpticalProperties top, Discretisation discrete) { + var pf = phaseFunctionSelector.newInstance(PhaseFunction.class, top, discrete); integrator.setPhaseFunction(pf); - pf.init(problem); + pf.init(top); firePropertyChanged(this, phaseFunctionSelector); } 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 548e488..846dc1b 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/DiscreteQuantities.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/DiscreteQuantities.java @@ -24,7 +24,7 @@ public DiscreteQuantities(int gridDensity, int ordinates) { init(gridDensity, ordinates); } - public void init(int gridDensity, int ordinates) { + protected final void init(int gridDensity, int ordinates) { I = new double[gridDensity + 1][ordinates]; f = new double[gridDensity + 1][ordinates]; Ik = new double[gridDensity + 1][ordinates]; @@ -32,7 +32,7 @@ public void init(int gridDensity, int ordinates) { qLast = new double[ordinates]; } - public void store() { + protected void store() { final int n = I.length; final int m = I[0].length; @@ -49,47 +49,47 @@ public void store() { } - public double[][] getIntensities() { + public final double[][] getIntensities() { return I; } - public double[][] getDerivatives() { + public final double[][] getDerivatives() { return f; } - public double getQLast(int i) { + protected final double getQLast(int i) { return qLast[i]; } - public void setQLast(int i, double q) { + protected final void setQLast(int i, double q) { this.qLast[i] = q; } - public double getDerivative(int i, int j) { + public final double getDerivative(int i, int j) { return f[i][j]; } - public void setDerivative(int i, int j, double f) { + public final void setDerivative(int i, int j, double f) { this.f[i][j] = f; } - public double getStoredIntensity(final int i, final int j) { + public final double getStoredIntensity(final int i, final int j) { return Ik[i][j]; } - public double getStoredDerivative(final int i, final int j) { + public final double getStoredDerivative(final int i, final int j) { return fk[i][j]; } - public void setStoredDerivative(final int i, final int j, final double f) { + public final void setStoredDerivative(final int i, final int j, final double f) { this.f[i][j] = f; } - public double getIntensity(int i, int j) { + public final double getIntensity(int i, int j) { return I[i][j]; } - public void setIntensity(int i, int j, double value) { + public final void setIntensity(int i, int j, double value) { I[i][j] = value; } 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 20776d3..932d7d6 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/Discretisation.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/Discretisation.java @@ -2,13 +2,10 @@ import static java.lang.Math.PI; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import pulse.io.readers.QuadratureReader; import pulse.problem.schemes.rte.BlackbodySpectrum; -import pulse.problem.statements.ParticipatingMedium; import pulse.problem.statements.model.ThermoOpticalProperties; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; @@ -38,9 +35,9 @@ public class Discretisation extends PropertyHolder { * Constructs a {@code DiscreteIntensities} with the default * {@code OrdinateSet} and a new uniform grid. * - * @param problem the problem statement + * @param properties */ - public Discretisation(ParticipatingMedium problem) { + public Discretisation(ThermoOpticalProperties properties) { quadSelector = new DiscreteSelector<>(QuadratureReader.getInstance(), "/quadratures/", "Quadratures.list"); @@ -52,10 +49,9 @@ public Discretisation(ParticipatingMedium problem) { this.firePropertyChanged(this, quadSelector); }); - var properties = (ThermoOpticalProperties) problem.getProperties(); setGrid(new StretchedGrid((double) properties.getOpticalThickness().getValue())); quantities = new DiscreteQuantities(grid.getDensity(), ordinates.getTotalNodes()); - setEmissivity((double) problem.getProperties().getEmissivity().getValue()); + setEmissivity((double) properties.getEmissivity().getValue()); } /** @@ -224,7 +220,7 @@ public void intensitiesRightBoundary(final BlackbodySpectrum ef) { } - protected void setEmissivity(double emissivity) { + protected final void setEmissivity(double emissivity) { this.emissivity = emissivity; boundaryFluxFactor = (1.0 - emissivity) / (emissivity * PI); } @@ -267,7 +263,7 @@ public StretchedGrid getGrid() { return grid; } - public void setGrid(StretchedGrid grid) { + public final void setGrid(StretchedGrid grid) { this.grid = grid; this.grid.setParent(this); } 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 246338a..648096e 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/ExplicitRungeKutta.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/ExplicitRungeKutta.java @@ -115,7 +115,7 @@ public Vector[] step(final int j, final double sign) { for (int l = n1; l < n2; l++) { // find unknown intensities (sum over the outward intensities) /* - * OUTWARD + * OUTWARD */ sum = tableau.getMatrix().get(m, 0) * q[l - n1][0]; for (int k = 1; k < m; k++) { 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 03c4477..f1e0d61 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/HenyeyGreensteinPF.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/HenyeyGreensteinPF.java @@ -3,6 +3,7 @@ import static java.lang.Math.sqrt; import pulse.problem.statements.ParticipatingMedium; +import pulse.problem.statements.model.ThermoOpticalProperties; /** * The single-parameter Henyey-Greenstein scattering phase function. @@ -14,13 +15,13 @@ public class HenyeyGreensteinPF extends PhaseFunction { private double a2; private double b1; - public HenyeyGreensteinPF(ParticipatingMedium medium, Discretisation intensities) { - super(medium, intensities); + public HenyeyGreensteinPF(ThermoOpticalProperties properties, Discretisation intensities) { + super(properties, intensities); } @Override - public void init(ParticipatingMedium problem) { - super.init(problem); + public void init(ThermoOpticalProperties properties) { + super.init(properties); final double anisotropy = getAnisotropyFactor(); b1 = 2.0 * anisotropy; final double aSq = anisotropy * anisotropy; @@ -30,9 +31,7 @@ public void init(ParticipatingMedium problem) { @Override public double function(final int i, final int k) { - final var ordinates = getDiscreteIntensities().getOrdinates(); - final double theta = ordinates.getNode(k) * ordinates.getNode(i); - final double f = a2 - b1 * theta; + final double f = a2 - b1 * cosineTheta(i, k); return a1 / (f * sqrt(f)); } 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 dc27701..7c382c6 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/LinearAnisotropicPF.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/LinearAnisotropicPF.java @@ -1,6 +1,7 @@ package pulse.problem.schemes.rte.dom; import pulse.problem.statements.ParticipatingMedium; +import pulse.problem.statements.model.ThermoOpticalProperties; /** * The linear-anisotropic scattering phase function. @@ -10,13 +11,13 @@ public class LinearAnisotropicPF extends PhaseFunction { private double g; - public LinearAnisotropicPF(ParticipatingMedium medium, Discretisation intensities) { - super(medium, intensities); + public LinearAnisotropicPF(ThermoOpticalProperties top, Discretisation intensities) { + super(top, intensities); } @Override - public void init(ParticipatingMedium medium) { - super.init(medium); + public void init(ThermoOpticalProperties top) { + super.init(top); g = 3.0 * getAnisotropyFactor(); } @@ -28,8 +29,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) { - final var ordinates = getDiscreteIntensities().getOrdinates(); - return 1.0 + g * ordinates.getNode(i) * ordinates.getNode(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 4c6680a..1785a2a 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/ODEIntegrator.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/ODEIntegrator.java @@ -2,7 +2,7 @@ import pulse.problem.schemes.rte.BlackbodySpectrum; import pulse.problem.schemes.rte.RTECalculationStatus; -import pulse.problem.statements.ParticipatingMedium; +import pulse.problem.statements.NonlinearProblem; import pulse.problem.statements.model.ThermoOpticalProperties; import pulse.util.PropertyHolder; import pulse.util.Reflexive; @@ -19,11 +19,14 @@ public ODEIntegrator(Discretisation intensities) { public abstract RTECalculationStatus integrate(); - protected void init(ParticipatingMedium problem) { - discretisation.setEmissivity((double) problem.getProperties().getEmissivity().getValue()); - var properties = (ThermoOpticalProperties) problem.getProperties(); + protected void init(NonlinearProblem problem) { + extract((ThermoOpticalProperties) problem.getProperties()); + setEmissionFunction( new BlackbodySpectrum(problem) ); + } + + protected void extract(ThermoOpticalProperties properties) { + discretisation.setEmissivity((double) properties.getEmissivity().getValue()); discretisation.setGrid(new StretchedGrid((double) properties.getOpticalThickness().getValue())); - setEmissionFunction(new BlackbodySpectrum(problem)); } protected void treatZeroIndex() { @@ -100,11 +103,11 @@ public double emission(double t) { return (1.0 - 2.0 * pf.getHalfAlbedo()) * spectrum.radianceAt(t); } - public PhaseFunction getPhaseFunction() { + public final PhaseFunction getPhaseFunction() { return pf; } - protected void setPhaseFunction(PhaseFunction pf) { + protected final void setPhaseFunction(PhaseFunction pf) { this.pf = pf; } @@ -118,20 +121,20 @@ public String toString() { return getClass().getSimpleName(); } - public Discretisation getDiscretisation() { + public final Discretisation getDiscretisation() { return discretisation; } - public void setDiscretisation(Discretisation discretisation) { + public final void setDiscretisation(Discretisation discretisation) { this.discretisation = discretisation; discretisation.setParent(this); } - public BlackbodySpectrum getEmissionFunction() { + public final BlackbodySpectrum getEmissionFunction() { return spectrum; } - public void setEmissionFunction(BlackbodySpectrum emissionFunction) { + public final void setEmissionFunction(BlackbodySpectrum emissionFunction) { this.spectrum = emissionFunction; } 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 f6c922b..68662f6 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/OrdinateSet.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/OrdinateSet.java @@ -59,7 +59,7 @@ public String printOrdinateSet() { } - public boolean hasZeroNode() { + public final boolean hasZeroNode() { return Arrays.stream(mu).anyMatch(Double.valueOf(0.0)::equals); } @@ -75,7 +75,7 @@ public String getName() { return name; } - public void setName(String name) { + public final void setName(String name) { this.name = name; } 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 0892aab..e8123f8 100644 --- a/src/main/java/pulse/problem/schemes/rte/dom/PhaseFunction.java +++ b/src/main/java/pulse/problem/schemes/rte/dom/PhaseFunction.java @@ -10,9 +10,22 @@ public abstract class PhaseFunction implements Reflexive { private double anisotropy; private double halfAlbedo; - public PhaseFunction(ParticipatingMedium medium, Discretisation intensities) { + public PhaseFunction(ThermoOpticalProperties top, Discretisation intensities) { this.intensities = intensities; - init(medium); + init(top); + } + + /** + * Calculates the cosine of the scattering angle as the product + * of the two discrete cosine nodes. + * @param i + * @param k + * @return + */ + + public final double cosineTheta(int i, int k) { + final var ordinates = getDiscreteIntensities().getOrdinates(); + return ordinates.getNode(k) * ordinates.getNode(i); } public double fullSum(int i, int j) { @@ -55,8 +68,7 @@ protected Discretisation getDiscreteIntensities() { return intensities; } - public void init(ParticipatingMedium problem) { - var properties = (ThermoOpticalProperties) problem.getProperties(); + public void init(ThermoOpticalProperties properties) { this.anisotropy = (double) properties.getScatteringAnisostropy().getValue(); this.halfAlbedo = 0.5 * (double) properties.getScatteringAlbedo().getValue(); } 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 aaa2c3d..bbcf0d3 100644 --- a/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringAnalyticalDerivatives.java +++ b/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringAnalyticalDerivatives.java @@ -30,8 +30,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}. + * selected numerical quadrature.Usually works best with the + {@code ChandrasekharsQuadrature} + * @return */ @Override public RTECalculationStatus compute(double U[]) { 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 7e72568..a4583b5 100644 --- a/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringRadiativeTransfer.java +++ b/src/main/java/pulse/problem/schemes/rte/exact/NonscatteringRadiativeTransfer.java @@ -17,7 +17,7 @@ public abstract class NonscatteringRadiativeTransfer extends RadiativeTransferSolver { - private static FunctionWithInterpolation ei3 = ExponentialIntegrals.get(3); + private static final FunctionWithInterpolation ei3 = ExponentialIntegrals.get(3); private double emissivity; @@ -27,7 +27,8 @@ public abstract class NonscatteringRadiativeTransfer extends RadiativeTransferSo private double radiosityFront; private double radiosityRear; - private InstanceDescriptor instanceDescriptor = new InstanceDescriptor( + private InstanceDescriptor instanceDescriptor + = new InstanceDescriptor( "Quadrature Selector", CompositionProduct.class); protected NonscatteringRadiativeTransfer(ParticipatingMedium problem, Grid grid) { @@ -48,7 +49,8 @@ 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. + * radiosities.A {@code NORMAL}status is always returned. + * @param array */ @Override public RTECalculationStatus compute(double[] array) { @@ -180,7 +182,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/ADILayeredSolver.java b/src/main/java/pulse/problem/schemes/solvers/ADILayeredSolver.java deleted file mode 100644 index 1f7a589..0000000 --- a/src/main/java/pulse/problem/schemes/solvers/ADILayeredSolver.java +++ /dev/null @@ -1,89 +0,0 @@ -package pulse.problem.schemes.solvers; - -import static pulse.properties.NumericProperties.def; -import static pulse.properties.NumericPropertyKeyword.SHELL_GRID_DENSITY; - -import java.util.HashMap; - -import pulse.problem.schemes.ADIScheme; -import pulse.problem.schemes.DifferenceScheme; -import pulse.problem.schemes.LayeredGrid2D; -import pulse.problem.schemes.Partition; -import pulse.problem.schemes.Partition.Location; -import pulse.problem.statements.CoreShellProblem; -import pulse.problem.statements.Problem; -import pulse.properties.NumericProperty; - -public class ADILayeredSolver extends ADIScheme implements Solver { - - public ADILayeredSolver() { - super(); - initGrid(getGrid().getGridDensity(), def(SHELL_GRID_DENSITY), getGrid().getTimeFactor()); - } - - public ADILayeredSolver(NumericProperty nCore, NumericProperty nShell, NumericProperty timeFactor) { - initGrid(nCore, nShell, timeFactor); - } - - public ADILayeredSolver(NumericProperty nCore, NumericProperty nShell, NumericProperty timeFactor, - NumericProperty timeLimit) { - setTimeLimit(timeLimit); - initGrid(nCore, nShell, timeFactor); - } - - public void initGrid(NumericProperty nCore, NumericProperty nShell, NumericProperty timeFactor) { - var map = new HashMap(); - map.put(Location.CORE_X, new Partition((int) nCore.getValue(), 1.0, 0.5)); - map.put(Location.CORE_Y, new Partition((int) nCore.getValue(), 1.0, 0.0)); - map.put(Location.FRONT_Y, new Partition((int) nShell.getValue(), 1.0, 0.0)); - map.put(Location.REAR_Y, new Partition((int) nShell.getValue(), 1.0, 0.0)); - map.put(Location.SIDE_X, new Partition((int) nShell.getValue(), 1.0, 0.0)); - map.put(Location.SIDE_Y, new Partition((int) nShell.getValue(), 1.0, 0.0)); - setGrid(new LayeredGrid2D(map, timeFactor)); - getGrid().setTimeFactor(timeFactor); - } - - private void prepareGrid(CoreShellProblem problem) { - var layeredGrid = (LayeredGrid2D) getGrid(); // TODO - layeredGrid.getPartition(Location.FRONT_Y).setGridMultiplier(problem.axialFactor()); - layeredGrid.getPartition(Location.REAR_Y).setGridMultiplier(problem.axialFactor()); - layeredGrid.getPartition(Location.SIDE_X).setGridMultiplier(problem.radialFactor()); - } - - @Override - public void solve(CoreShellProblem problem) { - prepareGrid(problem); - - // TODO - } - - @Override - public DifferenceScheme copy() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Class domain() { - return CoreShellProblem.class; - } - - @Override - public double signal() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public void timeStep(int m) { - // TODO Auto-generated method stub - - } - - @Override - public void finaliseStep() { - // TODO Auto-generated method stub - - } - -} diff --git a/src/main/java/pulse/problem/schemes/solvers/ADILinearisedSolver.java b/src/main/java/pulse/problem/schemes/solvers/ADILinearisedSolver.java index 7ce79f9..d2472df 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ADILinearisedSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ADILinearisedSolver.java @@ -77,15 +77,28 @@ 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]; + + U1_E = new double[N + 3][N + 3]; + U2_E = new double[N + 3][N + 3]; + + a1 = new double[N + 1]; + b1 = new double[N + 1]; + c1 = new double[N + 1]; + } - private void prepare(ClassicalProblem2D problem) { + @Override + public void prepare(Problem problem) throws SolverException { super.prepare(problem); var grid = getGrid(); tridiagonal = new TridiagonalMatrixAlgorithm(grid); - N = (int) grid.getGridDensity().getValue(); - hx = grid.getXStep(); hy = ((Grid2D) getGrid()).getYStep(); HX2 = hx * hx; @@ -104,15 +117,6 @@ private void prepare(ClassicalProblem2D problem) { l = (double) properties.getSampleThickness().getValue(); // end - 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]; - - a1 = new double[N + 1]; - b1 = new double[N + 1]; - c1 = new double[N + 1]; // a[i]*u[i-1] - b[i]*u[i] + c[i]*u[i+1] = F[i] lastIndex = (int) (fovOuter / d / hx); @@ -174,8 +178,8 @@ public DifferenceScheme copy() { } @Override - public Class domain() { - return ClassicalProblem2D.class; + public Class[] domain() { + return new Class[]{ClassicalProblem2D.class}; } @Override @@ -197,7 +201,6 @@ private void extendedU1(final int m) { for (int i = 0; i <= N; i++) { System.arraycopy(U1[i], 0, U1_E[i + 1], 1, N + 1); - U1_E[i + 1][0] = U1[i][1] + 2.0 * hy * pulse(m, i) - E_C_U1 * U1[i][0]; U1_E[i + 1][N + 2] = U1[i][N - 1] - E_C_U1 * U1[i][N]; } diff --git a/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolver.java b/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolver.java index 40927d7..d50b442 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolver.java @@ -1,67 +1,63 @@ package pulse.problem.schemes.solvers; -import static pulse.properties.NumericProperties.def; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericPropertyKeyword.GRID_DENSITY; -import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; import static pulse.properties.NumericPropertyKeyword.TAU_FACTOR; import static pulse.ui.Messages.getString; -import pulse.problem.schemes.DifferenceScheme; import pulse.problem.schemes.ExplicitScheme; -import pulse.problem.schemes.FixedPointIterations; import pulse.problem.schemes.RadiativeTransferCoupling; import pulse.problem.schemes.rte.Fluxes; import pulse.problem.schemes.rte.RTECalculationStatus; -import pulse.problem.schemes.rte.RadiativeTransferSolver; import pulse.problem.statements.ParticipatingMedium; import pulse.problem.statements.Problem; import pulse.problem.statements.model.ThermoOpticalProperties; import pulse.properties.NumericProperty; -public class ExplicitCoupledSolver extends ExplicitScheme implements Solver, FixedPointIterations { +public abstract class ExplicitCoupledSolver extends ExplicitScheme + implements Solver { private RadiativeTransferCoupling coupling; - private RadiativeTransferSolver rte; private RTECalculationStatus status; private Fluxes fluxes; private double hx; private double a; - private double nonlinearPrecision; - private double pls; private int N; private double HX_NP; private double prefactor; + 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)); } public ExplicitCoupledSolver(NumericProperty N, NumericProperty timeFactor) { super(N, timeFactor); - nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); setCoupling(new RadiativeTransferCoupling()); status = RTECalculationStatus.NORMAL; } - private void prepare(ParticipatingMedium problem) throws SolverException { + @Override + public void prepare(Problem problem) throws SolverException { super.prepare(problem); var grid = getGrid(); - coupling.init(problem, grid); - rte = coupling.getRadiativeTransferEquation(); + coupling.init((ParticipatingMedium)problem, grid); fluxes = coupling.getRadiativeTransferEquation().getFluxes(); setCalculationStatus(fluxes.checkArrays()); - + N = (int) grid.getGridDensity().getValue(); hx = grid.getXStep(); var p = (ThermoOpticalProperties) problem.getProperties(); - double Bi = (double) p.getHeatLoss().getValue(); + //combined Biot + double Bi = (double) p.getHeatLoss().getValue() + (double) p.getConvectiveLosses().getValue(); a = 1. / (1. + Bi * hx); @@ -71,46 +67,52 @@ private void prepare(ParticipatingMedium problem) throws SolverException { HX_NP = hx / Np; prefactor = tau * opticalThickness / Np; + + zeta = (double) ((ParticipatingMedium)problem).getGeometricFactor().getValue(); } @Override - public void solve(ParticipatingMedium problem) throws SolverException { - this.prepare(problem); - setCalculationStatus(coupling.getRadiativeTransferEquation().compute(getPreviousSolution())); - runTimeSequence(problem); + public void timeStep(int m) throws SolverException { + explicitSolution(); } @Override - public boolean normalOperation() { - return super.normalOperation() && (status == RTECalculationStatus.NORMAL); + public void finaliseStep() throws SolverException { + super.finaliseStep(); + if (autoUpdateFluxes) { + var rte = this.getCoupling().getRadiativeTransferEquation(); + setCalculationStatus(rte.compute(getCurrentSolution())); + } + coupling.getRadiativeTransferEquation().getFluxes().store(); } @Override - public void timeStep(int m) throws SolverException { - pls = pulse(m); - doIterations(getCurrentSolution(), nonlinearPrecision, m); + public void solve(ParticipatingMedium problem) throws SolverException { + prepare(problem); + runTimeSequence(problem); } @Override - public void iteration(final int m) { + public void explicitSolution() { /* * Uses the heat equation explicitly to calculate the grid-function everywhere * except the boundaries */ - explicitSolution(); + super.explicitSolution(); var V = getCurrentSolution(); + double pls = getCurrentPulseValue(); + // Front face - V[0] = (V[1] + hx * pls - HX_NP * fluxes.getFlux(0)) * a; + V[0] = (V[1] + hx * zeta * pls - HX_NP * fluxes.getFlux(0)) * a; // Rear face - V[N] = (V[N - 1] + HX_NP * fluxes.getFlux(N)) * a; + V[N] = (V[N - 1] + hx * (1.0 - zeta) * pls + HX_NP * fluxes.getFlux(N)) * a; } @Override - public void finaliseIteration(double[] V) throws SolverException { - FixedPointIterations.super.finaliseIteration(V); - setCalculationStatus(rte.compute(V)); + public boolean normalOperation() { + return super.normalOperation() && (status == RTECalculationStatus.NORMAL); } @Override @@ -118,12 +120,6 @@ public double phi(final int i) { return prefactor * fluxes.fluxDerivative(i); } - @Override - public void finaliseStep() throws SolverException { - super.finaliseStep(); - coupling.getRadiativeTransferEquation().getFluxes().store(); - } - public RadiativeTransferCoupling getCoupling() { return coupling; } @@ -142,22 +138,25 @@ public final void setCoupling(RadiativeTransferCoupling coupling) { public String toString() { return getString("ExplicitScheme.4"); } - + @Override - public DifferenceScheme copy() { - var grid = getGrid(); - return new ExplicitCoupledSolver(grid.getGridDensity(), grid.getTimeFactor()); + public Class[] domain() { + return new Class[]{ParticipatingMedium.class}; } - @Override - public Class domain() { - return ParticipatingMedium.class; - } - public void setCalculationStatus(RTECalculationStatus calculationStatus) throws SolverException { this.status = calculationStatus; - if(status != RTECalculationStatus.NORMAL) + if (status != RTECalculationStatus.NORMAL) { throw new SolverException(status.toString()); + } + } + + 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 new file mode 100644 index 0000000..ab30eeb --- /dev/null +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolverNL.java @@ -0,0 +1,93 @@ +/* + * Copyright 2022 Artem Lunev . + * + * 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.problem.schemes.solvers; + +import pulse.problem.schemes.DifferenceScheme; +import pulse.problem.schemes.FixedPointIterations; +import static pulse.properties.NumericProperties.def; +import static pulse.properties.NumericProperties.derive; +import pulse.properties.NumericProperty; +import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; +import pulse.ui.Messages; + +/** + * + * @author Artem Lunev + */ +public class ExplicitCoupledSolverNL extends ExplicitCoupledSolver + implements FixedPointIterations +{ + + 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); + } + + @Override + public void iteration(final int m) { + explicitSolution(); + } + + @Override + 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); + } + + public final void setNonlinearPrecision(NumericProperty nonlinearPrecision) { + this.nonlinearPrecision = (double) nonlinearPrecision.getValue(); + } + + @Override + public void set(NumericPropertyKeyword type, NumericProperty property) { + if (type == NONLINEAR_PRECISION) { + setNonlinearPrecision(property); + } else { + 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 5d3a6f9..7b7f0ae 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ExplicitLinearisedSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitLinearisedSolver.java @@ -64,7 +64,7 @@ public ExplicitLinearisedSolver(NumericProperty N, NumericProperty timeFactor, N } @Override - public void prepare(Problem problem) { + public void prepare(Problem problem) throws SolverException { super.prepare(problem); zeta = (double) ( (ClassicalProblem) problem).getGeometricFactor().getValue(); @@ -86,7 +86,7 @@ public void solve(ClassicalProblem problem) throws SolverException { public void timeStep(int m) { explicitSolution(); var V = getCurrentSolution(); - double pulse = pulse(m); + double pulse = getCurrentPulseValue(); setSolutionAt(0, (V[1] + hx * zeta * pulse) * a); setSolutionAt(N, (V[N - 1] + hx * (1.0 - zeta) * pulse) * a); } @@ -98,8 +98,8 @@ public DifferenceScheme copy() { } @Override - public Class domain() { - return ClassicalProblem.class; + public Class[] domain() { + return new Class[]{ClassicalProblem.class}; } } diff --git a/src/main/java/pulse/problem/schemes/solvers/ExplicitNonlinearSolver.java b/src/main/java/pulse/problem/schemes/solvers/ExplicitNonlinearSolver.java index 18dcb95..ed8e7fa 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ExplicitNonlinearSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitNonlinearSolver.java @@ -21,7 +21,6 @@ public class ExplicitNonlinearSolver extends ExplicitScheme implements Solver domain() { - return NonlinearProblem.class; + public Class[] domain() { + return new Class[]{NonlinearProblem.class}; } public NumericProperty getNonlinearPrecision() { diff --git a/src/main/java/pulse/problem/schemes/solvers/ExplicitTranslucentSolver.java b/src/main/java/pulse/problem/schemes/solvers/ExplicitTranslucentSolver.java index d22a20d..56e747c 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ExplicitTranslucentSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ExplicitTranslucentSolver.java @@ -18,10 +18,6 @@ public class ExplicitTranslucentSolver extends ExplicitScheme implements Solver< private double tau; private double a; - private double pls; - - private final static double EPS = 1e-7; // a small value ensuring numeric stability - private AbsorptionModel model; public ExplicitTranslucentSolver() { @@ -32,11 +28,12 @@ public ExplicitTranslucentSolver(NumericProperty N, NumericProperty timeFactor, super(N, timeFactor, timeLimit); } - private void prepare(PenetrationProblem problem) { + @Override + public void prepare(Problem problem) throws SolverException { super.prepare(problem); var grid = getGrid(); - model = problem.getAbsorptionModel(); + model = ((PenetrationProblem)problem).getAbsorptionModel(); N = (int) grid.getGridDensity().getValue(); hx = grid.getXStep(); @@ -54,8 +51,6 @@ public void solve(PenetrationProblem problem) throws SolverException { @Override public void timeStep(final int m) { - pls = this.pulse(m); - /* * Uses the heat equation explicitly to calculate the grid-function everywhere * except the boundaries @@ -73,7 +68,7 @@ public void timeStep(final int m) { @Override public double phi(final int i) { - return tau * pls * model.absorption(LASER, (i - EPS) * hx); + return tau * getCurrentPulseValue() * model.absorption(LASER, i * hx); } @Override @@ -93,8 +88,8 @@ public String toString() { } @Override - public Class domain() { - return PenetrationProblem.class; + public Class[] domain() { + return new Class[]{PenetrationProblem.class}; } @Override diff --git a/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolver.java b/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolver.java index 5bd30cc..98c3dbb 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolver.java @@ -6,16 +6,18 @@ import static pulse.ui.Messages.getString; import pulse.problem.schemes.CoupledImplicitScheme; -import pulse.problem.schemes.DifferenceScheme; import pulse.problem.schemes.TridiagonalMatrixAlgorithm; import pulse.problem.schemes.rte.Fluxes; import pulse.problem.schemes.rte.RTECalculationStatus; import pulse.problem.schemes.rte.RadiativeTransferSolver; +import pulse.problem.statements.ClassicalProblem; import pulse.problem.statements.ParticipatingMedium; +import pulse.problem.statements.Problem; import pulse.problem.statements.model.ThermoOpticalProperties; import pulse.properties.NumericProperty; -public class ImplicitCoupledSolver extends CoupledImplicitScheme implements Solver { +public abstract class ImplicitCoupledSolver extends CoupledImplicitScheme + implements Solver { private RadiativeTransferSolver rte; private Fluxes fluxes; @@ -30,6 +32,7 @@ public class ImplicitCoupledSolver extends CoupledImplicitScheme implements Solv private double HX_NP; private double v1; + private double zeta; public ImplicitCoupledSolver() { super(derive(GRID_DENSITY, 20), derive(TAU_FACTOR, 0.66667)); @@ -39,13 +42,14 @@ public ImplicitCoupledSolver(NumericProperty gridDensity, NumericProperty timeFa super(gridDensity, timeFactor, timeLimit); } - private void prepare(ParticipatingMedium problem) throws SolverException { + @Override + public void prepare(Problem problem) throws SolverException { super.prepare(problem); final var grid = getGrid(); var coupling = getCoupling(); - coupling.init(problem, grid); + coupling.init( (ParticipatingMedium) problem, grid); rte = coupling.getRadiativeTransferEquation(); N = (int) getGrid().getGridDensity().getValue(); @@ -54,7 +58,8 @@ private void prepare(ParticipatingMedium problem) throws SolverException { final double tau = grid.getTimeStep(); var p = (ThermoOpticalProperties) problem.getProperties(); - final double Bi1 = (double) p.getHeatLoss().getValue(); + //combined Biot + final double Bi1 = (double) p.getHeatLoss().getValue() + (double) p.getConvectiveLosses().getValue(); final double Np = (double) p.getPlanckNumber().getValue(); final double tau0 = (double) p.getOpticalThickness().getValue(); @@ -85,7 +90,8 @@ public double phi(int i) { tridiagonal.evaluateAlpha(); setTridiagonalMatrixAlgorithm(tridiagonal); - + + zeta = (double) ((ClassicalProblem)problem).getGeometricFactor().getValue(); } @Override @@ -108,30 +114,25 @@ public String toString() { } @Override - public DifferenceScheme copy() { - var grid = getGrid(); - return new ImplicitCoupledSolver(grid.getGridDensity(), grid.getTimeFactor(), getTimeLimit()); - } - - @Override - public double evalRightBoundary(final int m, final double alphaN, final double betaN) { + public double evalRightBoundary(final double alphaN, final double betaN) { /* * UNCOMMENT FOR A SIMPLIFIED CALCULATION * return (betaN + HX2_2TAU * getPreviousSolution()[N] + HX_2NP * (fluxes.getFlux(N - 1) + * fluxes.getFlux(N))) / (v1 - alphaN); */ - return (betaN + HX2_2TAU * getPreviousSolution()[N] + HX_NP * fluxes.getFlux(N) + return (betaN + HX2_2TAU * getPreviousSolution()[N] + hx * (1.0 - zeta) * getCurrentPulseValue() + + HX_NP * fluxes.getFlux(N) + HX2TAU0_2NP * fluxes.fluxDerivativeRear()) / (v1 - alphaN); } @Override - public double firstBeta(final int m) { + public double firstBeta() { /* * UNCOMMENT FOR A SIMPLIFIED CALCULATION * return (HX2_2TAU * getPreviousSolution()[0] + hx * getCurrentPulseValue() - HX_2NP * * (fluxes.getFlux(0) + fluxes.getFlux(1))) * alpha1; */ - return (HX2_2TAU * getPreviousSolution()[0] + hx * getCurrentPulseValue() + return (HX2_2TAU * getPreviousSolution()[0] + hx * zeta * getCurrentPulseValue() + HX2TAU0_2NP * fluxes.fluxDerivativeFront() - HX_NP * fluxes.getFlux(0)) * alpha1; } diff --git a/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolverNL.java b/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolverNL.java new file mode 100644 index 0000000..389d148 --- /dev/null +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolverNL.java @@ -0,0 +1,92 @@ +/* + * Copyright 2022 Artem Lunev . + * + * 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.problem.schemes.solvers; + +import pulse.problem.schemes.DifferenceScheme; +import pulse.problem.schemes.FixedPointIterations; +import static pulse.properties.NumericProperties.def; +import static pulse.properties.NumericProperties.derive; +import pulse.properties.NumericProperty; +import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; +import pulse.ui.Messages; + +/** + * + * @author Artem Lunev + */ +public class ImplicitCoupledSolverNL extends ImplicitCoupledSolver implements FixedPointIterations { + + private double nonlinearPrecision; + + public ImplicitCoupledSolverNL() { + super(); + nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); + setAutoUpdateFluxes(false); + } + + public ImplicitCoupledSolverNL(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) { + super(N, timeFactor, timeLimit); + nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); + setAutoUpdateFluxes(false); + } + + @Override + public void timeStep(final int m) throws SolverException { + doIterations(getCurrentSolution(), nonlinearPrecision, m); + } + + @Override + public void iteration(final int m) throws SolverException { + super.timeStep(m); + } + + @Override + public void finaliseIteration(double[] V) throws SolverException { + FixedPointIterations.super.finaliseIteration(V); + var rte = this.getCoupling().getRadiativeTransferEquation(); + setCalculationStatus(rte.compute(V)); + } + + public final NumericProperty getNonlinearPrecision() { + return derive(NONLINEAR_PRECISION, nonlinearPrecision); + } + + public final void setNonlinearPrecision(NumericProperty nonlinearPrecision) { + this.nonlinearPrecision = (double) nonlinearPrecision.getValue(); + } + + @Override + public void set(NumericPropertyKeyword type, NumericProperty property) { + if (type == NONLINEAR_PRECISION) { + setNonlinearPrecision(property); + } else { + 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 3e62848..920e4fb 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitDiathermicSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitDiathermicSolver.java @@ -3,6 +3,7 @@ import pulse.problem.schemes.BlockMatrixAlgorithm; import pulse.problem.schemes.DifferenceScheme; import pulse.problem.schemes.ImplicitScheme; +import pulse.problem.statements.ClassicalProblem; import pulse.problem.statements.DiathermicMedium; import pulse.problem.statements.Problem; import pulse.problem.statements.model.DiathermicProperties; @@ -16,8 +17,8 @@ public class ImplicitDiathermicSolver extends ImplicitScheme implements Solver domain() { - return DiathermicMedium.class; + public Class[] 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 6b3b0f6..5020d2c 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitLinearisedSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitLinearisedSolver.java @@ -68,7 +68,7 @@ public ImplicitLinearisedSolver(NumericProperty N, NumericProperty timeFactor, N } @Override - public void prepare(Problem problem) { + public void prepare(Problem problem) throws SolverException { super.prepare(problem); var grid = getGrid(); @@ -76,6 +76,7 @@ public void prepare(Problem problem) { N = (int) grid.getGridDensity().getValue(); final double hx = grid.getXStep(); tau = grid.getTimeStep(); + zeta = (double) ((ClassicalProblem)problem).getGeometricFactor().getValue(); final double Bi1 = (double) problem.getProperties().getHeatLoss().getValue(); @@ -105,16 +106,14 @@ public void solve(ClassicalProblem problem) throws SolverException { } @Override - public double firstBeta(final int m) { - final double pls = super.pulse(m); - return (HH * getPreviousSolution()[0] + _2HTAU * pls * zeta) / (2. * Bi1HTAU + 2. * tau + HH); + public double firstBeta() { + return (HH * getPreviousSolution()[0] + _2HTAU * getCurrentPulseValue() * zeta) / (2. * Bi1HTAU + 2. * tau + HH); } @Override - public double evalRightBoundary(final int m, final double alphaN, final double betaN) { - final double pls = super.pulse(m); + public double evalRightBoundary(final double alphaN, final double betaN) { return (HH * getPreviousSolution()[N] + 2. * tau * betaN - + _2HTAU * (1.0 - zeta) * pls //additional term due to stray light + + _2HTAU * (1.0 - zeta) * getCurrentPulseValue() //additional term due to stray light ) / (2 * Bi1HTAU + HH - 2. * tau * (alphaN - 1)); } @@ -125,8 +124,8 @@ public DifferenceScheme copy() { } @Override - public Class domain() { - return ClassicalProblem.class; + 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 f61fa05..0cc048a 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitNonlinearSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitNonlinearSolver.java @@ -3,9 +3,6 @@ import static pulse.math.MathUtils.fastPowLoop; import static pulse.properties.NumericProperties.def; import static pulse.properties.NumericProperties.derive; -import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; - -import java.util.List; import java.util.Set; import pulse.problem.schemes.DifferenceScheme; @@ -17,14 +14,12 @@ import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; -import pulse.properties.Property; public class ImplicitNonlinearSolver extends ImplicitScheme implements Solver, FixedPointIterations { private int N; private double HH; private double tau; - private double pls; private double dT_T; @@ -51,7 +46,8 @@ public ImplicitNonlinearSolver(NumericProperty N, NumericProperty timeFactor, Nu nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); } - private void prepare(NonlinearProblem problem) { + @Override + public void prepare(Problem problem) throws SolverException { super.prepare(problem); var grid = getGrid(); @@ -101,8 +97,8 @@ public DifferenceScheme copy() { } @Override - public Class domain() { - return NonlinearProblem.class; + public Class[] domain() { + return new Class[]{NonlinearProblem.class}; } public NumericProperty getNonlinearPrecision() { @@ -133,7 +129,6 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { @Override public void timeStep(final int m) throws SolverException { - pls = pulse(m); doIterations(getCurrentSolution(), nonlinearPrecision, m); } @@ -143,14 +138,15 @@ public void iteration(int m) throws SolverException { } @Override - public double evalRightBoundary(int m, double alphaN, double betaN) { + public double evalRightBoundary(double alphaN, double betaN) { return c2 * (2. * betaN * tau + HH * getPreviousSolution()[N] + c1 * (fastPowLoop(getCurrentSolution()[N] * dT_T + 1, 4) - 1)); } @Override - public double firstBeta(int m) { - return b1 * getPreviousSolution()[0] + b2 * (pls - b3 * (fastPowLoop(getCurrentSolution()[0] * dT_T + 1, 4) - 1)); + public double firstBeta() { + return b1 * getPreviousSolution()[0] + b2 * (getCurrentPulseValue() + - b3 * (fastPowLoop(getCurrentSolution()[0] * 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 5a769b7..d5005ba 100644 --- a/src/main/java/pulse/problem/schemes/solvers/ImplicitTranslucentSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/ImplicitTranslucentSolver.java @@ -5,29 +5,22 @@ import static pulse.ui.Messages.getString; import pulse.problem.schemes.DifferenceScheme; -import pulse.problem.schemes.Grid; import pulse.problem.schemes.ImplicitScheme; import pulse.problem.schemes.TridiagonalMatrixAlgorithm; import pulse.problem.statements.PenetrationProblem; import pulse.problem.statements.Problem; import pulse.problem.statements.model.AbsorptionModel; -import pulse.problem.statements.model.BeerLambertAbsorption; import pulse.properties.NumericProperty; public class ImplicitTranslucentSolver extends ImplicitScheme implements Solver { private AbsorptionModel absorption; - private Grid grid; - private double pls; private int N; private double HH; private double _2Bi1HTAU; private double b11; - private double frontAbsorption; - private double rearAbsorption; - public ImplicitTranslucentSolver() { super(); } @@ -36,31 +29,28 @@ public ImplicitTranslucentSolver(NumericProperty N, NumericProperty timeFactor, super(N, timeFactor, timeLimit); } - private void prepare(PenetrationProblem problem) { + @Override + public void prepare(Problem problem) throws SolverException { super.prepare(problem); - grid = getGrid(); + var grid = getGrid(); final double tau = grid.getTimeStep(); N = (int) grid.getGridDensity().getValue(); final double Bi1H = (double) problem.getProperties().getHeatLoss().getValue() * grid.getXStep(); final double hx = grid.getXStep(); - absorption = 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)); - - final double EPS = 1E-7; - rearAbsorption = tau * absorption.absorption(LASER, (N - EPS) * hx); - frontAbsorption = tau * absorption.absorption(LASER, 0.0) + 2.0*tau/hx; - + var tridiagonal = new TridiagonalMatrixAlgorithm(grid) { @Override public double phi(final int i) { - return pls * absorption.absorption(LASER, (i - EPS) * hx); + return getCurrentPulseValue() * absorption.absorption(LASER, i * hx); } }; @@ -81,28 +71,25 @@ public void solve(PenetrationProblem problem) throws SolverException { runTimeSequence(problem); } - @Override - public void timeStep(final int m) throws SolverException { - pls = pulse(m); - super.timeStep(m); - } - @Override public double signal() { - return evaluateSignal(absorption, grid, getCurrentSolution()); + return evaluateSignal(absorption, getGrid(), getCurrentSolution()); } @Override - public double evalRightBoundary(final int m, final double alphaN, final double betaN) { - final double tau = grid.getTimeStep(); + public double evalRightBoundary(final double alphaN, final double betaN) { + final double tau = getGrid().getTimeStep(); + var tridiagonal = this.getTridiagonalMatrixAlgorithm(); - return (HH * (getPreviousSolution()[N] + pls * rearAbsorption) + return (HH * getPreviousSolution()[N] + HH*tau*tridiagonal.phi(N) + 2. * tau * betaN) / (_2Bi1HTAU + HH + 2. * tau * (1 - alphaN)); } @Override - public double firstBeta(int m) { - return (getPreviousSolution()[0] + pls * frontAbsorption) * b11; + public double firstBeta() { + var tridiagonal = this.getTridiagonalMatrixAlgorithm(); + double tau = getGrid().getTimeStep(); + return (getPreviousSolution()[0] + tau*tridiagonal.phi(0))* b11; } @Override @@ -122,8 +109,8 @@ public String toString() { } @Override - public Class domain() { - return PenetrationProblem.class; + public Class[] domain() { + return new Class[]{PenetrationProblem.class}; } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolver.java b/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolver.java index 463ee02..fc3e516 100644 --- a/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolver.java @@ -10,16 +10,17 @@ import java.util.Set; import pulse.problem.schemes.CoupledImplicitScheme; -import pulse.problem.schemes.DifferenceScheme; import pulse.problem.schemes.TridiagonalMatrixAlgorithm; import pulse.problem.schemes.rte.RadiativeTransferSolver; +import pulse.problem.statements.ClassicalProblem; import pulse.problem.statements.ParticipatingMedium; +import pulse.problem.statements.Problem; import pulse.problem.statements.model.ThermoOpticalProperties; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import pulse.ui.Messages; -public class MixedCoupledSolver extends CoupledImplicitScheme implements Solver { +public abstract class MixedCoupledSolver extends CoupledImplicitScheme + implements Solver { private RadiativeTransferSolver rte; @@ -47,6 +48,7 @@ public class MixedCoupledSolver extends CoupledImplicitScheme implements Solver< private double _2TAU_ONE_MINUS_SIGMA; private double BETA1_FACTOR; private double ONE_MINUS_SIGMA; + private double zeta; public MixedCoupledSolver() { super(derive(GRID_DENSITY, 16), derive(TAU_FACTOR, 0.25)); @@ -58,21 +60,27 @@ public MixedCoupledSolver(NumericProperty N, NumericProperty timeFactor, Numeric sigma = (double) def(SCHEME_WEIGHT).getValue(); } - private void prepare(ParticipatingMedium problem) throws SolverException { + @Override + public void prepare(Problem problem) throws SolverException { super.prepare(problem); var grid = getGrid(); var coupling = getCoupling(); - coupling.init(problem, grid); + coupling.init((ParticipatingMedium) problem, grid); rte = coupling.getRadiativeTransferEquation(); N = (int) grid.getGridDensity().getValue(); hx = grid.getXStep(); tau = grid.getTimeStep(); - Bi1 = (double) problem.getProperties().getHeatLoss().getValue(); - + var properties = (ThermoOpticalProperties)problem.getProperties(); + //combined biot + Bi1 = (double) properties.getHeatLoss().getValue() + + (double) properties.getConvectiveLosses().getValue(); + + zeta = (double) ( (ClassicalProblem)problem ).getGeometricFactor().getValue(); + var tridiagonal = new TridiagonalMatrixAlgorithm(grid) { @Override @@ -107,7 +115,7 @@ public void evaluateBeta(final double[] U) { setTridiagonalMatrixAlgorithm(tridiagonal); } - private void initConst(ParticipatingMedium problem) { + private void initConst(ClassicalProblem problem) { var p = (ThermoOpticalProperties) problem.getProperties(); final double Np = (double) p.getPlanckNumber().getValue(); final double opticalThickness = (double) p.getOpticalThickness().getValue(); @@ -155,21 +163,23 @@ public double pulse(final int m) { } @Override - public double firstBeta(final int m) { + public double firstBeta() { var fluxes = rte.getFluxes(); var U = getPreviousSolution(); final double phi = TAU0_NP * fluxes.fluxDerivativeFront(); return (_2TAUHX - * (getCurrentPulseValue() - 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; + * (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; } @Override - public double evalRightBoundary(final int m, final double alphaN, final double betaN) { + public double evalRightBoundary(final double alphaN, final double betaN) { var fluxes = rte.getFluxes(); final double phi = TAU0_NP * fluxes.fluxDerivativeRear(); final var U = getPreviousSolution(); - return (sigma * betaN + HX2_2TAU * U[N] + 0.5 * HX2 * phi + return (sigma * betaN + hx * getCurrentPulseValue() * (1.0 - zeta) + HX2_2TAU * U[N] + 0.5 * HX2 * phi + ONE_MINUS_SIGMA * (U[N - 1] - U[N] * ONE_PLUS_Bi1_HX) + HX_NP * (sigma * fluxes.getFlux(N) + ONE_MINUS_SIGMA * fluxes.getStoredFlux(N))) / (HX2_2TAU + sigma * (ONE_PLUS_Bi1_HX - alphaN)); @@ -205,15 +215,4 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { } } - @Override - public String toString() { - return Messages.getString("MixedScheme2.4"); - } - - @Override - public DifferenceScheme copy() { - var grid = getGrid(); - return new MixedCoupledSolver(grid.getGridDensity(), grid.getTimeFactor(), getTimeLimit()); - } - } \ 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 new file mode 100644 index 0000000..77d7b19 --- /dev/null +++ b/src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolverNL.java @@ -0,0 +1,92 @@ +/* + * Copyright 2022 Artem Lunev . + * + * 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.problem.schemes.solvers; + +import pulse.problem.schemes.DifferenceScheme; +import pulse.problem.schemes.FixedPointIterations; +import static pulse.properties.NumericProperties.def; +import static pulse.properties.NumericProperties.derive; +import pulse.properties.NumericProperty; +import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; +import pulse.ui.Messages; + +/** + * + * @author Artem Lunev + */ +public class MixedCoupledSolverNL extends MixedCoupledSolver implements FixedPointIterations { + + private double nonlinearPrecision; + + public MixedCoupledSolverNL() { + super(); + nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); + setAutoUpdateFluxes(false); + } + + public MixedCoupledSolverNL(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) { + super(N, timeFactor, timeLimit); + nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue(); + setAutoUpdateFluxes(false); + } + + @Override + public void timeStep(final int m) throws SolverException { + doIterations(getCurrentSolution(), nonlinearPrecision, m); + } + + @Override + public void iteration(final int m) throws SolverException { + super.timeStep(m); + } + + @Override + public void finaliseIteration(double[] V) throws SolverException { + FixedPointIterations.super.finaliseIteration(V); + var rte = this.getCoupling().getRadiativeTransferEquation(); + setCalculationStatus(rte.compute(V)); + } + + public final NumericProperty getNonlinearPrecision() { + return derive(NONLINEAR_PRECISION, nonlinearPrecision); + } + + public final void setNonlinearPrecision(NumericProperty nonlinearPrecision) { + this.nonlinearPrecision = (double) nonlinearPrecision.getValue(); + } + + @Override + public void set(NumericPropertyKeyword type, NumericProperty property) { + if (type == NONLINEAR_PRECISION) { + setNonlinearPrecision(property); + } else { + 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 979d50f..56a74f0 100644 --- a/src/main/java/pulse/problem/schemes/solvers/MixedLinearisedSolver.java +++ b/src/main/java/pulse/problem/schemes/solvers/MixedLinearisedSolver.java @@ -55,6 +55,8 @@ public class MixedLinearisedSolver extends MixedScheme implements Solver domain() { - return ClassicalProblem.class; + public Class[] domain() { + return new Class[]{ClassicalProblem.class}; } } diff --git a/src/main/java/pulse/problem/statements/ClassicalProblem2D.java b/src/main/java/pulse/problem/statements/ClassicalProblem2D.java index d85462b..278fe8e 100644 --- a/src/main/java/pulse/problem/statements/ClassicalProblem2D.java +++ b/src/main/java/pulse/problem/statements/ClassicalProblem2D.java @@ -8,6 +8,8 @@ import pulse.math.ParameterVector; import pulse.math.Segment; import pulse.math.transforms.InvDiamTransform; +import pulse.math.transforms.StickTransform; +import pulse.math.transforms.Transformable; import pulse.problem.laser.DiscretePulse; import pulse.problem.laser.DiscretePulse2D; import pulse.problem.schemes.ADIScheme; @@ -18,7 +20,6 @@ import pulse.problem.statements.model.ExtendedThermalProperties; import pulse.problem.statements.model.ThermalProperties; import pulse.properties.Flag; -import static pulse.properties.NumericPropertyKeyword.HEAT_LOSS; import static pulse.properties.NumericPropertyKeyword.HEAT_LOSS_SIDE; import pulse.ui.Messages; @@ -66,7 +67,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, List flags) { super.optimisationVector(output, flags); @@ -76,7 +77,9 @@ public void optimisationVector(ParameterVector output, List flags) { for (int i = 0, size = output.dimension(); i < size; i++) { var key = output.getIndex(i); - + Transformable transform = new InvDiamTransform(properties); + var bounds = Segment.boundsFrom(key); + switch (key) { case FOV_OUTER: value = (double) properties.getFOVOuter().getValue(); @@ -88,20 +91,20 @@ public void optimisationVector(ParameterVector output, List flags) { value = (double) ((Pulse2D) getPulse()).getSpotDiameter().getValue(); break; case HEAT_LOSS_SIDE: - final double Bi = (double) properties.getSideLosses().getValue(); - setHeatLossParameter(output, i, Bi); - continue; + value = (double) properties.getSideLosses().getValue(); + transform = new StickTransform(bounds); + break; case HEAT_LOSS_COMBINED: - final double combined = (double) properties.getHeatLoss().getValue(); - setHeatLossParameter(output, i, combined); - continue; + value = (double) properties.getHeatLoss().getValue(); + transform = new StickTransform(bounds); + break; default: continue; } - output.setTransform(i, new InvDiamTransform(properties)); + output.setTransform(i, transform); + output.setParameterBounds(i, bounds); output.set(i, value); - output.setParameterBounds(i, new Segment(0.5 * value, 1.5 * value)); } @@ -141,4 +144,4 @@ public Problem copy() { return new ClassicalProblem2D(this); } -} +} \ No newline at end of file diff --git a/src/main/java/pulse/problem/statements/CoreShellProblem.java b/src/main/java/pulse/problem/statements/CoreShellProblem.java deleted file mode 100644 index d575a39..0000000 --- a/src/main/java/pulse/problem/statements/CoreShellProblem.java +++ /dev/null @@ -1,164 +0,0 @@ -package pulse.problem.statements; - -import static pulse.properties.NumericProperties.def; -import static pulse.properties.NumericProperties.derive; -import static pulse.properties.NumericPropertyKeyword.AXIAL_COATING_THICKNESS; -import static pulse.properties.NumericPropertyKeyword.COATING_DIFFUSIVITY; -import static pulse.properties.NumericPropertyKeyword.RADIAL_COATING_THICKNESS; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import pulse.math.ParameterVector; -import pulse.math.Segment; -import pulse.math.transforms.InvDiamTransform; -import pulse.math.transforms.InvLenSqTransform; -import pulse.math.transforms.InvLenTransform; -import pulse.problem.schemes.solvers.SolverException; -import pulse.problem.statements.model.ExtendedThermalProperties; -import pulse.properties.Flag; -import pulse.properties.NumericProperty; -import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.NONLINEAR_PRECISION; -import pulse.properties.Property; -import pulse.ui.Messages; - -public class CoreShellProblem extends ClassicalProblem2D { - - private double tA; - private double tR; - private double coatingDiffusivity; - private final static boolean DEBUG = true; - - public CoreShellProblem() { - super(); - tA = (double) def(AXIAL_COATING_THICKNESS).getValue(); - tR = (double) def(RADIAL_COATING_THICKNESS).getValue(); - coatingDiffusivity = (double) def(COATING_DIFFUSIVITY).getValue(); - setComplexity(ProblemComplexity.HIGH); - } - - @Override - public String toString() { - return Messages.getString("UniformlyCoatedSample.Descriptor"); - } - - public NumericProperty getCoatingAxialThickness() { - return derive(AXIAL_COATING_THICKNESS, tA); - } - - public NumericProperty getCoatingRadialThickness() { - return derive(RADIAL_COATING_THICKNESS, tR); - } - - public double axialFactor() { - return tA / (double) getProperties().getSampleThickness().getValue(); - } - - public double radialFactor() { - return tR / (double) getProperties().getSampleThickness().getValue(); - } - - public void setCoatingAxialThickness(NumericProperty t) { - this.tA = (double) t.getValue(); - } - - public void setCoatingRadialThickness(NumericProperty t) { - this.tR = (double) t.getValue(); - } - - public NumericProperty getCoatingDiffusivity() { - return derive(COATING_DIFFUSIVITY, coatingDiffusivity); - } - - public void setCoatingDiffusivity(NumericProperty a) { - this.coatingDiffusivity = (double) a.getValue(); - } - - @Override - public Set listedKeywords() { - var set = super.listedKeywords(); - set.add(AXIAL_COATING_THICKNESS); - set.add(RADIAL_COATING_THICKNESS); - set.add(COATING_DIFFUSIVITY); - return set; - } - - @Override - public void set(NumericPropertyKeyword type, NumericProperty property) { - switch (type) { - case COATING_DIFFUSIVITY: - setCoatingDiffusivity(property); - break; - case AXIAL_COATING_THICKNESS: - setCoatingAxialThickness(property); - break; - case RADIAL_COATING_THICKNESS: - setCoatingRadialThickness(property); - break; - default: - super.set(type, property); - break; - } - } - - @Override - public boolean isEnabled() { - return !DEBUG; - } - - @Override - public void optimisationVector(ParameterVector output, List flags) { - super.optimisationVector(output, flags); - - var bounds = new Segment(0.1, 1.0); - var properties = (ExtendedThermalProperties) this.getProperties(); - - for (int i = 0, size = output.dimension(); i < size; i++) { - var key = output.getIndex(i); - switch (key) { - case AXIAL_COATING_THICKNESS: - output.setTransform(i, new InvLenTransform(properties)); - output.set(i, tA); - output.setParameterBounds(i, bounds); - break; - case RADIAL_COATING_THICKNESS: - output.setTransform(i, new InvDiamTransform(properties)); - output.set(i, tR); - output.setParameterBounds(i, bounds); - break; - case COATING_DIFFUSIVITY: - output.setTransform(i, new InvLenSqTransform(properties)); - output.set(i, coatingDiffusivity); - output.setParameterBounds(i, new Segment(0.5 * coatingDiffusivity, 1.5 * coatingDiffusivity)); - break; - default: - continue; - } - } - - } - - @Override - public void assign(ParameterVector params) throws SolverException { - super.assign(params); - - for (int i = 0, size = params.dimension(); i < size; i++) { - switch (params.getIndex(i)) { - case AXIAL_COATING_THICKNESS: - tA = params.inverseTransform(i); - break; - case RADIAL_COATING_THICKNESS: - tR = params.inverseTransform(i); - break; - case COATING_DIFFUSIVITY: - coatingDiffusivity = params.inverseTransform(i); - break; - default: - continue; - } - } - } - -} diff --git a/src/main/java/pulse/problem/statements/DiathermicMedium.java b/src/main/java/pulse/problem/statements/DiathermicMedium.java index b2aca28..488eafd 100644 --- a/src/main/java/pulse/problem/statements/DiathermicMedium.java +++ b/src/main/java/pulse/problem/statements/DiathermicMedium.java @@ -2,7 +2,6 @@ import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericPropertyKeyword.DIATHERMIC_COEFFICIENT; -import static pulse.properties.NumericPropertyKeyword.NUMPOINTS; import java.util.List; @@ -15,6 +14,7 @@ import pulse.problem.statements.model.DiathermicProperties; import pulse.problem.statements.model.ThermalProperties; import pulse.properties.Flag; +import static pulse.properties.NumericPropertyKeyword.HEAT_LOSS_CONVECTIVE; import pulse.ui.Messages; /** @@ -61,17 +61,31 @@ public void optimisationVector(ParameterVector output, List flags) { for (int i = 0, size = output.dimension(); i < size; i++) { var key = output.getIndex(i); + Segment bounds = null; + double value = 0; - if (key == DIATHERMIC_COEFFICIENT) { - - var bounds = new Segment(0.0, 1.0); - final double etta = (double) properties.getDiathermicCoefficient().getValue(); - - output.setTransform(i, new StickTransform(bounds)); - output.set(i, etta); - output.setParameterBounds(i, bounds); - + switch (key) { + case DIATHERMIC_COEFFICIENT: + bounds = Segment.boundsFrom(DIATHERMIC_COEFFICIENT); + value = (double) properties.getDiathermicCoefficient().getValue(); + break; + case HEAT_LOSS_CONVECTIVE: + bounds = Segment.boundsFrom(HEAT_LOSS_CONVECTIVE); + 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; + } + default: + continue; } + + output.setTransform(i, new StickTransform(bounds)); + output.set(i, value); + output.setParameterBounds(i, bounds); } @@ -91,17 +105,10 @@ public void assign(ParameterVector params) throws SolverException { case DIATHERMIC_COEFFICIENT: properties.setDiathermicCoefficient(derive(DIATHERMIC_COEFFICIENT, params.inverseTransform(i))); break; - case HEAT_LOSS: - if (properties.areThermalPropertiesLoaded()) { - properties.calculateEmissivity(); - final double emissivity = (double) properties.getEmissivity().getValue(); - properties - .setDiathermicCoefficient(derive(DIATHERMIC_COEFFICIENT, emissivity / (2.0 - emissivity))); - } + case HEAT_LOSS_CONVECTIVE: + properties.setConvectiveLosses(derive(HEAT_LOSS_CONVECTIVE, params.inverseTransform(i))); break; default: - continue; - } } @@ -123,4 +130,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 5097ab3..28b2857 100644 --- a/src/main/java/pulse/problem/statements/NonlinearProblem.java +++ b/src/main/java/pulse/problem/statements/NonlinearProblem.java @@ -21,6 +21,7 @@ import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; import static pulse.properties.NumericPropertyKeyword.LASER_ENERGY; +import static pulse.properties.NumericPropertyKeyword.SOURCE_GEOMETRIC_FACTOR; import pulse.ui.Messages; public class NonlinearProblem extends ClassicalProblem { @@ -54,6 +55,7 @@ public Set listedKeywords() { set.add(SPECIFIC_HEAT); set.add(DENSITY); set.remove(SPOT_DIAMETER); + set.remove(SOURCE_GEOMETRIC_FACTOR); return set; } diff --git a/src/main/java/pulse/problem/statements/ParticipatingMedium.java b/src/main/java/pulse/problem/statements/ParticipatingMedium.java index 5674dcc..ad0d177 100644 --- a/src/main/java/pulse/problem/statements/ParticipatingMedium.java +++ b/src/main/java/pulse/problem/statements/ParticipatingMedium.java @@ -1,21 +1,18 @@ package pulse.problem.statements; -import static pulse.properties.NumericProperties.derive; import java.util.List; +import java.util.Set; import pulse.math.ParameterVector; -import pulse.math.Segment; -import pulse.math.transforms.StickTransform; -import pulse.math.transforms.Transformable; import pulse.problem.schemes.DifferenceScheme; import pulse.problem.schemes.solvers.MixedCoupledSolver; import pulse.problem.schemes.solvers.SolverException; import pulse.problem.statements.model.ThermalProperties; import pulse.problem.statements.model.ThermoOpticalProperties; import pulse.properties.Flag; -import static pulse.properties.NumericPropertyKeyword.OPTICAL_THICKNESS; -import static pulse.properties.NumericPropertyKeyword.PLANCK_NUMBER; +import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.SOURCE_GEOMETRIC_FACTOR; import pulse.ui.Messages; public class ParticipatingMedium extends NonlinearProblem { @@ -39,72 +36,14 @@ public String toString() { public void optimisationVector(ParameterVector output, List flags) { super.optimisationVector(output, flags); var properties = (ThermoOpticalProperties) getProperties(); - - Segment bounds = null; - double value; - Transformable transform; - - for (int i = 0, size = output.dimension(); i < size; i++) { - - var key = output.getIndex(i); - - switch (key) { - case PLANCK_NUMBER: - final double lowerBound = Segment.boundsFrom(PLANCK_NUMBER).getMinimum(); - bounds = new Segment(lowerBound, properties.maxNp()); - value = (double) properties.getPlanckNumber().getValue(); - break; - case OPTICAL_THICKNESS: - value = (double) properties.getOpticalThickness().getValue(); - bounds = Segment.boundsFrom(OPTICAL_THICKNESS); - break; - case SCATTERING_ALBEDO: - value = (double) properties.getScatteringAlbedo().getValue(); - bounds = new Segment(0.0, 1.0); - break; - case SCATTERING_ANISOTROPY: - value = (double) properties.getScatteringAnisostropy().getValue(); - bounds = new Segment(-1.0, 1.0); - break; - default: - continue; - - } - - transform = new StickTransform(bounds); - output.setTransform(i, transform); - output.set(i, value); - output.setParameterBounds(i, bounds); - - } - + properties.optimisationVector(output, flags); } @Override public void assign(ParameterVector params) throws SolverException { super.assign(params); - var properties = (ThermoOpticalProperties) getProperties(); - - for (int i = 0, size = params.dimension(); i < size; i++) { - - var type = params.getIndex(i); - - switch (type) { - - case PLANCK_NUMBER: - case SCATTERING_ALBEDO: - case SCATTERING_ANISOTROPY: - case OPTICAL_THICKNESS: - properties.set(type, derive(type, params.inverseTransform(i))); - break; - default: - break; - - } - - } - + properties.assign(params); } @Override @@ -122,9 +61,16 @@ public void initProperties() { setProperties(new ThermoOpticalProperties()); } + @Override + public Set listedKeywords() { + var set = super.listedKeywords(); + set.add(SOURCE_GEOMETRIC_FACTOR); + return set; + } + @Override 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 46a2b84..b93e68f 100644 --- a/src/main/java/pulse/problem/statements/PenetrationProblem.java +++ b/src/main/java/pulse/problem/statements/PenetrationProblem.java @@ -1,6 +1,7 @@ package pulse.problem.statements; import java.util.List; +import java.util.Set; import pulse.math.ParameterVector; import pulse.problem.schemes.DifferenceScheme; @@ -9,6 +10,8 @@ import pulse.problem.statements.model.AbsorptionModel; import pulse.problem.statements.model.BeerLambertAbsorption; import pulse.properties.Flag; +import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.SOURCE_GEOMETRIC_FACTOR; import pulse.properties.Property; import pulse.ui.Messages; import pulse.util.InstanceDescriptor; @@ -16,9 +19,8 @@ public class PenetrationProblem extends ClassicalProblem { private InstanceDescriptor instanceDescriptor - = new InstanceDescriptor( + = new InstanceDescriptor<>( "Absorption Model Selector", AbsorptionModel.class); - private AbsorptionModel absorption = instanceDescriptor.newInstance(AbsorptionModel.class); public PenetrationProblem() { @@ -55,6 +57,13 @@ public List listedTypes() { list.add(instanceDescriptor); return list; } + + @Override + public Set listedKeywords() { + var set = super.listedKeywords(); + set.remove(SOURCE_GEOMETRIC_FACTOR); + return set; + } public InstanceDescriptor getAbsorptionSelector() { return instanceDescriptor; diff --git a/src/main/java/pulse/problem/statements/Problem.java b/src/main/java/pulse/problem/statements/Problem.java index d0e8adc..d2bde96 100644 --- a/src/main/java/pulse/problem/statements/Problem.java +++ b/src/main/java/pulse/problem/statements/Problem.java @@ -1,5 +1,6 @@ package pulse.problem.statements; +import java.util.Arrays; import static pulse.input.listeners.CurveEventType.RESCALED; import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericPropertyKeyword.TIME_SHIFT; @@ -15,8 +16,6 @@ import pulse.math.ParameterVector; import pulse.math.Segment; import pulse.math.transforms.InvLenSqTransform; -import pulse.math.transforms.StandardTransformations; -import static pulse.math.transforms.StandardTransformations.ABS; import pulse.math.transforms.StickTransform; import pulse.problem.laser.DiscretePulse; import pulse.problem.schemes.DifferenceScheme; @@ -58,7 +57,8 @@ public abstract class Problem extends PropertyHolder implements Reflexive, Optim private static boolean hideDetailedAdjustment = true; private ProblemComplexity complexity = ProblemComplexity.LOW; - private InstanceDescriptor instanceDescriptor = new InstanceDescriptor( + private InstanceDescriptor instanceDescriptor + = new InstanceDescriptor<>( "Baseline Selector", Baseline.class); /** @@ -73,7 +73,6 @@ public abstract class Problem extends PropertyHolder implements Reflexive, Optim */ protected Problem() { initProperties(); - setHeatingCurve(new HeatingCurve()); instanceDescriptor.attemptUpdate(LinearBaseline.class.getSimpleName()); @@ -100,7 +99,7 @@ public Problem(Problem p) { public abstract Problem copy(); - public void setHeatingCurve(HeatingCurve curve) { + public final void setHeatingCurve(HeatingCurve curve) { this.curve = curve; curve.setParent(this); } @@ -112,10 +111,7 @@ private void addListeners() { }); curve.addHeatingCurveListener(e -> { if (e.getType() == RESCALED) { - var c = e.getData(); - if (!c.isIncomplete()) { - curve.apply(getBaseline()); - } + curve.apply(getBaseline()); } }); } @@ -134,9 +130,10 @@ private void addListeners() { * @return a {@code List} of available {@code DifferenceScheme}s for solving * this {@code Problem}. */ - public List availableSolutions() { + public final List availableSolutions() { var allSchemes = Reflexive.instancesOf(DifferenceScheme.class); - return allSchemes.stream().filter(scheme -> scheme instanceof Solver).filter(s -> s.domain() == this.getClass()) + return allSchemes.stream().filter(scheme -> scheme instanceof Solver) + .filter(s -> Arrays.asList(s.domain()).contains(this.getClass()) ) .collect(Collectors.toList()); } @@ -152,7 +149,7 @@ public void set(NumericPropertyKeyword type, NumericProperty value) { properties.set(type, value); } - public HeatingCurve getHeatingCurve() { + public final HeatingCurve getHeatingCurve() { return curve; } @@ -228,48 +225,47 @@ public void optimisationVector(ParameterVector output, List flags) { var key = output.getIndex(i); + Segment bounds = Segment.boundsFrom(key); + double value = 0; + switch (key) { case THICKNESS: - final double l = (double) properties.getSampleThickness().getValue(); - var bounds = Segment.boundsFrom(THICKNESS); - output.setParameterBounds(i, bounds); - output.setTransform(i, new StickTransform(bounds)); - output.set(i, l); + value = (double) properties.getSampleThickness().getValue(); break; case DIFFUSIVITY: final double a = (double) properties.getDiffusivity().getValue(); output.setTransform(i, new InvLenSqTransform(properties)); - output.setParameterBounds(i, new Segment(0.33 * a, 3.0 * a)); + bounds = new Segment(0.01 * a, 20.0 * a); + output.setParameterBounds(i, bounds); output.set(i, a); - break; + //custom transform here -- skip assigning StickTransform + continue; case MAXTEMP: final double signalHeight = (double) properties.getMaximumTemperature().getValue(); - output.setTransform(i, ABS); - output.setParameterBounds(i, new Segment(0.5 * signalHeight, 1.5 * signalHeight)); - output.set(i, signalHeight); + bounds = new Segment(0.5 * signalHeight, 1.5 * signalHeight); + value = signalHeight; break; case HEAT_LOSS: - final double Bi = (double) properties.getHeatLoss().getValue(); - output.setParameterBounds(i, Segment.boundsFrom(HEAT_LOSS)); - setHeatLossParameter(output, i, Bi); + value = (double) properties.getHeatLoss().getValue(); + output.setTransform(i, new StickTransform(bounds)); break; case TIME_SHIFT: - output.set(i, (double) curve.getTimeShift().getValue()); double magnitude = 0.25 * properties.timeFactor(); - output.setParameterBounds(i, new Segment(-magnitude, magnitude)); + bounds = new Segment(-magnitude, magnitude); + value = (double) curve.getTimeShift().getValue(); break; default: + continue; } - + + output.setTransform(i, new StickTransform(bounds)); + output.setParameterBounds(i, bounds); + output.set(i, value); + } } - protected void setHeatLossParameter(ParameterVector output, int i, double Bi) { - output.setTransform(i, StandardTransformations.ABS); - output.set(i, Bi); - } - /** * Assigns parameter values of this {@code Problem} using the optimisation * vector {@code params}. Only those parameters will be updated, the types @@ -293,21 +289,21 @@ public void assign(ParameterVector params) throws SolverException { for (int i = 0, size = params.dimension(); i < size; i++) { - double value = params.get(i); + double value = params.inverseTransform(i); var key = params.getIndex(i); switch (key) { case THICKNESS: - properties.setSampleThickness(derive(THICKNESS, params.inverseTransform(i) )); + properties.setSampleThickness(derive(THICKNESS, value )); break; case DIFFUSIVITY: - properties.setDiffusivity(derive(DIFFUSIVITY, params.inverseTransform(i))); + properties.setDiffusivity(derive(DIFFUSIVITY, value)); break; case MAXTEMP: properties.setMaximumTemperature(derive(MAXTEMP, value)); break; case HEAT_LOSS: - properties.setHeatLoss(derive(HEAT_LOSS, params.inverseTransform(i))); + properties.setHeatLoss(derive(HEAT_LOSS, value)); break; case TIME_SHIFT: curve.set(TIME_SHIFT, derive(TIME_SHIFT, value)); @@ -337,14 +333,10 @@ public boolean areDetailsHidden() { * @param b {@code true} if the user does not want to see the details, * {@code false} otherwise. */ - public static void setDetailsHidden(boolean b) { + public final static void setDetailsHidden(boolean b) { Problem.hideDetailedAdjustment = b; } - public String shortName() { - return getClass().getSimpleName(); - } - /** * Used for debugging. Initially, the nonlinear and two-dimensional problem * statements are disabled, since they have not yet been thoroughly tested @@ -419,11 +411,10 @@ public Baseline getBaseline() { * @param baseline the new baseline. * @see pulse.baseline.Baseline.apply(Baseline) */ - public void setBaseline(Baseline baseline) { + public final void setBaseline(Baseline baseline) { this.baseline = baseline; - if (!curve.isIncomplete()) { - curve.apply(baseline); - } + curve.apply(baseline); + baseline.setParent(this); var searchTask = (SearchTask) this.specificAncestor(SearchTask.class); @@ -433,7 +424,7 @@ public void setBaseline(Baseline baseline) { } } - public InstanceDescriptor getBaselineDescriptor() { + public final InstanceDescriptor getBaselineDescriptor() { return instanceDescriptor; } @@ -443,7 +434,7 @@ private void initBaseline() { parameterListChanged(); } - public ThermalProperties getProperties() { + public final ThermalProperties getProperties() { return properties; } @@ -460,4 +451,4 @@ public final void setProperties(ThermalProperties properties) { public abstract boolean isReady(); -} +} \ No newline at end of file diff --git a/src/main/java/pulse/problem/statements/Pulse.java b/src/main/java/pulse/problem/statements/Pulse.java index cec8937..d2b5324 100644 --- a/src/main/java/pulse/problem/statements/Pulse.java +++ b/src/main/java/pulse/problem/statements/Pulse.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Set; +import pulse.input.ExperimentalData; import pulse.problem.laser.PulseTemporalShape; import pulse.problem.laser.RectangularPulse; @@ -90,9 +91,17 @@ private void addListeners() { NumericProperty pw = NumericProperties .derive(NumericPropertyKeyword.LOWER_BOUND, (Number) np.getValue()); + + var range = corrTask.getExperimentalCurve().getRange(); + + if( range.getLowerBound().compareTo(pw) < 0 ) { + //update lower bound of the range for that SearchTask corrTask.getExperimentalCurve().getRange() .setLowerBound(pw); + + } + } } @@ -123,21 +132,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 (Math.abs((newValue - this.pulseWidth) - / (this.pulseWidth + newValue)) < EPS) { - return; - } - - //validate -- do not update if the new pulse width is greater than 2 half-times - var task = (SearchTask) this.specificAncestor(SearchTask.class); - var data = task.getExperimentalCurve(); - if (newValue > 0 && newValue < 2.0 * data.getHalfTime()) { - this.pulseWidth = (double) pulseWidth.getValue(); - firePropertyChanged(this, pulseWidth); + 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 = task.getExperimentalCurve(); + + if(newValue < 2.0 * data.getHalfTime()) { + this.pulseWidth = (double) pulseWidth.getValue(); + firePropertyChanged(this, pulseWidth); + } + } + } public NumericProperty getLaserEnergy() { @@ -205,7 +217,7 @@ public PulseTemporalShape getPulseShape() { public void setPulseShape(PulseTemporalShape pulseShape) { this.pulseShape = pulseShape; - pulseShape.setParent(this); + pulseShape.setParent(this); } } diff --git a/src/main/java/pulse/problem/statements/model/AbsorptionModel.java b/src/main/java/pulse/problem/statements/model/AbsorptionModel.java index 434db3c..3560faf 100644 --- a/src/main/java/pulse/problem/statements/model/AbsorptionModel.java +++ b/src/main/java/pulse/problem/statements/model/AbsorptionModel.java @@ -111,7 +111,6 @@ public void optimisationVector(ParameterVector output, List flags) { double value = 0; Transformable transform = ABS; - output.setParameterBounds(i, new Segment(1E-2, 1000.0)); switch (key) { case LASER_ABSORPTIVITY: @@ -128,6 +127,7 @@ public void optimisationVector(ParameterVector output, List flags) { } //do this for the listed key values + output.setParameterBounds(i, Segment.boundsFrom(key)); output.setTransform(i, transform); output.set(i, value); diff --git a/src/main/java/pulse/problem/statements/model/DiathermicProperties.java b/src/main/java/pulse/problem/statements/model/DiathermicProperties.java index 2315390..bc0a9a5 100644 --- a/src/main/java/pulse/problem/statements/model/DiathermicProperties.java +++ b/src/main/java/pulse/problem/statements/model/DiathermicProperties.java @@ -7,14 +7,17 @@ import static pulse.properties.NumericProperty.requireType; import pulse.properties.NumericPropertyKeyword; import static pulse.properties.NumericPropertyKeyword.DIATHERMIC_COEFFICIENT; +import static pulse.properties.NumericPropertyKeyword.HEAT_LOSS_CONVECTIVE; public class DiathermicProperties extends ThermalProperties { private double diathermicCoefficient; + private double convectiveLosses; public DiathermicProperties() { super(); this.diathermicCoefficient = (double) def(DIATHERMIC_COEFFICIENT).getValue(); + this.convectiveLosses = (double) def(HEAT_LOSS_CONVECTIVE).getValue(); } public DiathermicProperties(ThermalProperties p) { @@ -23,8 +26,10 @@ public DiathermicProperties(ThermalProperties p) { ? ((DiathermicProperties) p).getDiathermicCoefficient() : def(DIATHERMIC_COEFFICIENT); this.diathermicCoefficient = (double) property.getValue(); + this.convectiveLosses = (double) property.getValue(); } + @Override public ThermalProperties copy() { return new ThermalProperties(this); } @@ -37,13 +42,29 @@ public void setDiathermicCoefficient(NumericProperty diathermicCoefficient) { requireType(diathermicCoefficient, DIATHERMIC_COEFFICIENT); this.diathermicCoefficient = (double) diathermicCoefficient.getValue(); } + + public NumericProperty getConvectiveLosses() { + return derive(HEAT_LOSS_CONVECTIVE, convectiveLosses); + } + + public void setConvectiveLosses(NumericProperty convectiveLosses) { + requireType(convectiveLosses, HEAT_LOSS_CONVECTIVE); + this.convectiveLosses = (double) convectiveLosses.getValue(); + } @Override public void set(NumericPropertyKeyword type, NumericProperty property) { - if (type == DIATHERMIC_COEFFICIENT) { - diathermicCoefficient = ((Number) property.getValue()).doubleValue(); - } else { - super.set(type, property); + double value = ((Number) property.getValue()).doubleValue(); + switch (type) { + case DIATHERMIC_COEFFICIENT: + diathermicCoefficient = value; + break; + case HEAT_LOSS_CONVECTIVE: + convectiveLosses = value; + break; + default: + super.set(type, property); + break; } } @@ -51,7 +72,8 @@ public void set(NumericPropertyKeyword type, NumericProperty property) { public Set listedKeywords() { var set = super.listedKeywords(); set.add(DIATHERMIC_COEFFICIENT); + set.add(HEAT_LOSS_CONVECTIVE); 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 a0790c3..4ff6ff7 100644 --- a/src/main/java/pulse/problem/statements/model/ExtendedThermalProperties.java +++ b/src/main/java/pulse/problem/statements/model/ExtendedThermalProperties.java @@ -7,8 +7,6 @@ import static pulse.properties.NumericPropertyKeyword.FOV_OUTER; import static pulse.properties.NumericPropertyKeyword.HEAT_LOSS_SIDE; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import pulse.input.ExperimentalData; @@ -16,10 +14,8 @@ import static pulse.properties.NumericProperties.def; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.DIATHERMIC_COEFFICIENT; import static pulse.properties.NumericPropertyKeyword.HEAT_LOSS; import static pulse.properties.NumericPropertyKeyword.HEAT_LOSS_COMBINED; -import pulse.properties.Property; public class ExtendedThermalProperties extends ThermalProperties { @@ -66,7 +62,7 @@ public ThermalProperties copy() { public void useTheoreticalEstimates(ExperimentalData c) { super.useTheoreticalEstimates(c); if (areThermalPropertiesLoaded()) { - Bi3 = biot(); + Bi3 = radiationBiot(); } } diff --git a/src/main/java/pulse/problem/statements/model/ThermalProperties.java b/src/main/java/pulse/problem/statements/model/ThermalProperties.java index 1039f25..c3691eb 100644 --- a/src/main/java/pulse/problem/statements/model/ThermalProperties.java +++ b/src/main/java/pulse/problem/statements/model/ThermalProperties.java @@ -134,6 +134,7 @@ public boolean areDetailsHidden() { * allowed to use those types of {@code NumericPropery} that are listed by * the {@code listedParameters()}. * + * @param value * @see listedTypes() */ @Override @@ -168,6 +169,9 @@ public void set(NumericPropertyKeyword type, NumericProperty value) { public void setHeatLoss(NumericProperty Bi) { requireType(Bi, HEAT_LOSS); this.Bi = (double) Bi.getValue(); + if(areThermalPropertiesLoaded()) { + calculateEmissivity(); + } firePropertyChanged(this, Bi); } @@ -222,6 +226,7 @@ public NumericProperty getSpecificHeat() { public void setSpecificHeat(NumericProperty cP) { requireType(cP, SPECIFIC_HEAT); this.cP = (double) cP.getValue(); + firePropertyChanged(this, cP); } public NumericProperty getDensity() { @@ -231,6 +236,7 @@ public NumericProperty getDensity() { public void setDensity(NumericProperty p) { requireType(p, DENSITY); this.rho = (double) (p.getValue()); + firePropertyChanged(this, p); } public NumericProperty getTestTemperature() { @@ -269,6 +275,7 @@ public Set listedKeywords() { set.add(HEAT_LOSS); set.add(DENSITY); set.add(SPECIFIC_HEAT); + set.add(EMISSIVITY); return set; } @@ -282,16 +289,33 @@ public NumericProperty getThermalConductivity() { public void calculateEmissivity() { double newEmissivity = Bi * thermalConductivity() / (4. * Math.pow(T, 3) * l * STEFAN_BOTLZMAN); - var transform = new StickTransform(new Segment(0.01, 1.0)); + var transform = new StickTransform(Segment.boundsFrom(EMISSIVITY)); setEmissivity(derive(EMISSIVITY, transform.transform(newEmissivity)) ); } - public double biot() { + /** + * 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. + * @return the maximum Biot number + */ + + public double maxRadiationBiot() { + double absMax = Segment.boundsFrom(HEAT_LOSS).getMaximum(); + return emissivity > 0 ? radiationBiot() / emissivity : absMax; + } /** * Performs simple calculation of the l2/a @@ -317,7 +341,7 @@ public void useTheoreticalEstimates(ExperimentalData c) { final double t0 = c.getHalfTime(); this.a = PARKERS_COEFFICIENT * l * l / t0; if (areThermalPropertiesLoaded()) { - Bi = biot(); + Bi = radiationBiot(); } } @@ -338,6 +362,7 @@ public NumericProperty getEmissivity() { public void setEmissivity(NumericProperty e) { requireType(e, EMISSIVITY); this.emissivity = (double) e.getValue(); + firePropertyChanged(this, e); } @Override @@ -355,4 +380,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 feafb90..67db88b 100644 --- a/src/main/java/pulse/problem/statements/model/ThermoOpticalProperties.java +++ b/src/main/java/pulse/problem/statements/model/ThermoOpticalProperties.java @@ -1,49 +1,62 @@ package pulse.problem.statements.model; +import java.util.List; import static pulse.math.MathUtils.fastPowLoop; import static pulse.properties.NumericProperties.def; -import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericProperty.requireType; -import static pulse.properties.NumericPropertyKeyword.OPTICAL_THICKNESS; -import static pulse.properties.NumericPropertyKeyword.PLANCK_NUMBER; import static pulse.properties.NumericPropertyKeyword.SCATTERING_ALBEDO; import static pulse.properties.NumericPropertyKeyword.SCATTERING_ANISOTROPY; import java.util.Set; import pulse.input.ExperimentalData; +import pulse.math.ParameterVector; +import pulse.math.Segment; +import pulse.math.transforms.StickTransform; +import pulse.math.transforms.Transformable; +import pulse.problem.schemes.solvers.SolverException; +import pulse.properties.Flag; +import static pulse.properties.NumericProperties.derive; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; +import static pulse.properties.NumericPropertyKeyword.HEAT_LOSS_CONVECTIVE; +import static pulse.properties.NumericPropertyKeyword.OPTICAL_THICKNESS; +import static pulse.properties.NumericPropertyKeyword.PLANCK_NUMBER; +import pulse.search.Optimisable; -public class ThermoOpticalProperties extends ThermalProperties { +public class ThermoOpticalProperties extends ThermalProperties implements Optimisable { private double opticalThickness; private double planckNumber; private double scatteringAlbedo; private double scatteringAnisotropy; + private double convectiveLosses; 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(); + 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); - this.opticalThickness = (double) def(OPTICAL_THICKNESS).getValue(); - this.planckNumber = (double) def(PLANCK_NUMBER).getValue(); - scatteringAlbedo = (double) def(SCATTERING_ALBEDO).getValue(); - scatteringAnisotropy = (double) def(SCATTERING_ANISOTROPY).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.opticalThickness = p.opticalThickness; + this.planckNumber = p.planckNumber; + this.scatteringAlbedo = p.scatteringAlbedo; + this.scatteringAnisotropy = p.scatteringAnisotropy; + this.convectiveLosses = p.convectiveLosses; } @Override @@ -68,6 +81,9 @@ public void set(NumericPropertyKeyword type, NumericProperty value) { case SCATTERING_ANISOTROPY: setScatteringAnisotropy(value); break; + case HEAT_LOSS_CONVECTIVE: + setConvectiveLosses(value); + break; default: break; } @@ -81,6 +97,7 @@ public Set listedKeywords() { set.add(OPTICAL_THICKNESS); set.add(SCATTERING_ALBEDO); set.add(SCATTERING_ANISOTROPY); + set.add(HEAT_LOSS_CONVECTIVE); return set; } @@ -119,7 +136,17 @@ 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(); + firePropertyChanged(this, losses); + } + public NumericProperty getConvectiveLosses() { + return derive(HEAT_LOSS_CONVECTIVE, convectiveLosses); + } + public NumericProperty getScatteringAlbedo() { return derive(SCATTERING_ALBEDO, scatteringAlbedo); } @@ -151,6 +178,7 @@ public String getDescriptor() { @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); + sb.append(String.format("%n %-25s", this.getConvectiveLosses())); sb.append(String.format("%n %-25s", this.getOpticalThickness())); sb.append(String.format("%n %-25s", this.getPlanckNumber())); sb.append(String.format("%n %-25s", this.getScatteringAlbedo())); @@ -159,5 +187,80 @@ public String toString() { sb.append(String.format("%n %-25s", this.getDensity())); return sb.toString(); } + + @Override + public void optimisationVector(ParameterVector output, List flags) { + Segment bounds = null; + double value; + Transformable transform; + + for (int i = 0, size = output.dimension(); i < size; i++) { + + var key = output.getIndex(i); + + switch (key) { + case PLANCK_NUMBER: + final double lowerBound = Segment.boundsFrom(PLANCK_NUMBER).getMinimum(); + bounds = new Segment(lowerBound, maxNp()); + value = planckNumber; + break; + case OPTICAL_THICKNESS: + value = opticalThickness; + bounds = Segment.boundsFrom(OPTICAL_THICKNESS); + break; + case SCATTERING_ALBEDO: + value = scatteringAlbedo; + bounds = Segment.boundsFrom(SCATTERING_ALBEDO); + break; + case SCATTERING_ANISOTROPY: + value = scatteringAnisotropy; + bounds = Segment.boundsFrom(SCATTERING_ANISOTROPY); + break; + case HEAT_LOSS_CONVECTIVE: + value = convectiveLosses; + bounds = Segment.boundsFrom(HEAT_LOSS_CONVECTIVE); + break; + case HEAT_LOSS: + value = (double) getHeatLoss().getValue(); + bounds = new Segment(0.0, maxRadiationBiot() ); + break; + default: + continue; + + } + + transform = new StickTransform(bounds); + output.setTransform(i, transform); + output.set(i, value); + output.setParameterBounds(i, bounds); + + } + + } + + @Override + public void assign(ParameterVector params) throws SolverException { + + for (int i = 0, size = params.dimension(); i < size; i++) { + + var type = params.getIndex(i); + + switch (type) { + + case PLANCK_NUMBER: + case SCATTERING_ALBEDO: + case SCATTERING_ANISOTROPY: + case OPTICAL_THICKNESS: + case HEAT_LOSS_CONVECTIVE: + set(type, derive(type, params.inverseTransform(i))); + break; + default: + break; + + } + + } + + } } diff --git a/src/main/java/pulse/properties/NumericPropertyKeyword.java b/src/main/java/pulse/properties/NumericPropertyKeyword.java index c9e63cb..539a832 100644 --- a/src/main/java/pulse/properties/NumericPropertyKeyword.java +++ b/src/main/java/pulse/properties/NumericPropertyKeyword.java @@ -157,6 +157,13 @@ 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. diff --git a/src/main/java/pulse/properties/SampleName.java b/src/main/java/pulse/properties/SampleName.java index 04cb0bc..5ef7419 100644 --- a/src/main/java/pulse/properties/SampleName.java +++ b/src/main/java/pulse/properties/SampleName.java @@ -7,7 +7,7 @@ public class SampleName implements Property { private String name; public SampleName() { - name = "Nameless"; + //null name } @Override diff --git a/src/main/java/pulse/search/direction/ComplexPath.java b/src/main/java/pulse/search/direction/ComplexPath.java index 9cf4d47..da33ddd 100644 --- a/src/main/java/pulse/search/direction/ComplexPath.java +++ b/src/main/java/pulse/search/direction/ComplexPath.java @@ -1,10 +1,8 @@ package pulse.search.direction; -import pulse.math.ParameterVector; import static pulse.math.linear.Matrices.createIdentityMatrix; import pulse.math.linear.SquareMatrix; -import pulse.problem.schemes.solvers.SolverException; import pulse.tasks.SearchTask; /** @@ -28,13 +26,13 @@ protected ComplexPath(SearchTask task) { * In addition to the superclass method, resets the Hessian to an Identity * matrix. * - * @throws SolverException + * @param task */ @Override public void configure(SearchTask task) { - super.configure(task); hessian = createIdentityMatrix(ActiveFlags.activeParameters(task).size()); inverseHessian = createIdentityMatrix(hessian.getData().length); + super.configure(task); } public SquareMatrix getHessian() { diff --git a/src/main/java/pulse/search/direction/CompositePathOptimiser.java b/src/main/java/pulse/search/direction/CompositePathOptimiser.java index 9b4c0bf..3b78913 100644 --- a/src/main/java/pulse/search/direction/CompositePathOptimiser.java +++ b/src/main/java/pulse/search/direction/CompositePathOptimiser.java @@ -1,6 +1,5 @@ package pulse.search.direction; -import java.util.Arrays; import static pulse.properties.NumericProperties.compare; import java.util.List; @@ -17,7 +16,8 @@ public abstract class CompositePathOptimiser extends GradientBasedOptimiser { - private InstanceDescriptor instanceDescriptor = new InstanceDescriptor( + private InstanceDescriptor instanceDescriptor + = new InstanceDescriptor( "Linear Optimiser Selector", LinearOptimiser.class); private LinearOptimiser linearSolver; @@ -45,6 +45,7 @@ private void initLinearOptimiser() { setLinearSolver(instanceDescriptor.newInstance(LinearOptimiser.class)); } + @Override public boolean iteration(SearchTask task) throws SolverException { var p = (GradientGuidedPath) task.getIterativeState(); // the previous state of the task @@ -71,11 +72,8 @@ public boolean iteration(SearchTask task) throws SolverException { // new set of parameters determined through search var candidateParams = parameters.sum(dir.multiply(step)); - if( Arrays.stream( candidateParams.getData() ).anyMatch(el -> !Double.isFinite(el) ) ) { - throw new SolverException("Illegal candidate parameters: not finite! " + p.getIteration()); - } - task.assign(new ParameterVector(parameters, candidateParams)); // assign new parameters + double newCost = task.solveProblemAndCalculateCost(); // calculate the sum of squared residuals if (newCost > initialCost - EPS diff --git a/src/main/java/pulse/search/direction/GradientGuidedPath.java b/src/main/java/pulse/search/direction/GradientGuidedPath.java index 22a0e05..9dcab90 100644 --- a/src/main/java/pulse/search/direction/GradientGuidedPath.java +++ b/src/main/java/pulse/search/direction/GradientGuidedPath.java @@ -1,8 +1,11 @@ package pulse.search.direction; +import java.util.logging.Level; +import java.util.logging.Logger; import pulse.math.linear.Vector; import pulse.problem.schemes.solvers.SolverException; import pulse.tasks.SearchTask; +import pulse.tasks.logs.Status; /** *

@@ -35,18 +38,18 @@ protected GradientGuidedPath(SearchTask t) { /** * Resets the {@code Path}: calculates the current gradient and the - * direction of search. Sets the minimum point to 0.0. + * direction of search.Sets the minimum point to 0.0. * * @param t the {@code SearchTask}, for which this {@code Path} is created. + * @throws pulse.problem.schemes.solvers.SolverException * @see pulse.search.direction.PathSolver.direction(Path) */ public void configure(SearchTask t) { super.reset(); try { this.gradient = ((GradientBasedOptimiser) PathOptimiser.getInstance()).gradient(t); - } catch (SolverException e) { - System.err.println("Failed on gradient calculation while resetting optimiser..."); - e.printStackTrace(); + } catch (SolverException ex) { + t.notifyFailedStatus(ex); } minimumPoint = 0.0; } diff --git a/src/main/java/pulse/search/direction/PathOptimiser.java b/src/main/java/pulse/search/direction/PathOptimiser.java index b11adc0..d357115 100644 --- a/src/main/java/pulse/search/direction/PathOptimiser.java +++ b/src/main/java/pulse/search/direction/PathOptimiser.java @@ -6,16 +6,13 @@ import static pulse.properties.NumericPropertyKeyword.ERROR_TOLERANCE; import static pulse.properties.NumericPropertyKeyword.ITERATION_LIMIT; -import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import pulse.problem.schemes.solvers.SolverException; import pulse.properties.Flag; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.GRADIENT_RESOLUTION; import pulse.properties.Property; import pulse.search.statistics.OptimiserStatistic; import pulse.tasks.SearchTask; diff --git a/src/main/java/pulse/search/linear/LinearOptimiser.java b/src/main/java/pulse/search/linear/LinearOptimiser.java index 5588f78..891b42b 100644 --- a/src/main/java/pulse/search/linear/LinearOptimiser.java +++ b/src/main/java/pulse/search/linear/LinearOptimiser.java @@ -5,9 +5,6 @@ import static pulse.properties.NumericProperties.derive; import static pulse.properties.NumericPropertyKeyword.LINEAR_RESOLUTION; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.Set; import pulse.math.ParameterVector; @@ -16,8 +13,6 @@ import pulse.problem.schemes.solvers.SolverException; import pulse.properties.NumericProperty; import pulse.properties.NumericPropertyKeyword; -import static pulse.properties.NumericPropertyKeyword.GRADIENT_RESOLUTION; -import pulse.properties.Property; import pulse.tasks.SearchTask; import pulse.util.PropertyHolder; import pulse.util.Reflexive; @@ -71,7 +66,7 @@ protected LinearOptimiser() { */ public static Segment domain(ParameterVector x, Vector p) { double alphaMax = Double.POSITIVE_INFINITY; - double alpha = 0.0; + double alpha; for (int i = 0; i < x.dimension(); i++) { @@ -94,7 +89,9 @@ public static Segment domain(ParameterVector x, Vector p) { } - return new Segment(0.0, alphaMax); + //check that alphaMax is not zero! otherwise the optimise will crash + return new Segment(0.0, + Math.max(alphaMax, 1E-10)); } diff --git a/src/main/java/pulse/search/linear/WolfeOptimiser.java b/src/main/java/pulse/search/linear/WolfeOptimiser.java index b4db6d7..751d7d6 100644 --- a/src/main/java/pulse/search/linear/WolfeOptimiser.java +++ b/src/main/java/pulse/search/linear/WolfeOptimiser.java @@ -24,7 +24,7 @@ * page */ public class WolfeOptimiser extends LinearOptimiser { - + private static WolfeOptimiser instance = new WolfeOptimiser(); /** @@ -37,7 +37,7 @@ public class WolfeOptimiser extends LinearOptimiser { * gradient projection, equal to {@value C2}. */ public final static double C2 = 0.8; - + private WolfeOptimiser() { super(); } @@ -65,32 +65,33 @@ private WolfeOptimiser() { */ @Override public double linearStep(SearchTask 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(); Segment segment = domain(params, direction); - + double cost1 = task.solveProblemAndCalculateCost(); - + double randomConfinedValue = 0; double g2p; - - var instance = (GradientBasedOptimiser) PathOptimiser.getInstance(); - + + var optimiser = (GradientBasedOptimiser) PathOptimiser.getInstance(); + for (double initialLength = segment.length(); segment.length() / initialLength > searchResolution;) { - + randomConfinedValue = segment.randomValue(); - + final var newParams = params.sum(direction.multiply(randomConfinedValue)); + task.assign(new ParameterVector(params, newParams)); - + final double cost2 = task.solveProblemAndCalculateCost(); /** @@ -102,8 +103,8 @@ public double linearStep(SearchTask task) throws SolverException { segment.setMaximum(randomConfinedValue); continue; } - - final var g2 = instance.gradient(task); + + final var g2 = optimiser.gradient(task); g2p = g2.dot(direction); /** @@ -118,16 +119,16 @@ public double linearStep(SearchTask 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$ @@ -142,5 +143,5 @@ public String toString() { public static WolfeOptimiser getInstance() { return instance; } - + } diff --git a/src/main/java/pulse/search/statistics/CorrelationTest.java b/src/main/java/pulse/search/statistics/CorrelationTest.java index 67191bc..c977544 100644 --- a/src/main/java/pulse/search/statistics/CorrelationTest.java +++ b/src/main/java/pulse/search/statistics/CorrelationTest.java @@ -15,8 +15,8 @@ public abstract class CorrelationTest extends PropertyHolder implements Reflexiv private static double threshold = (double) def(CORRELATION_THRESHOLD).getValue(); - private static InstanceDescriptor instanceDescriptor - = new InstanceDescriptor( + private static final InstanceDescriptor instanceDescriptor + = new InstanceDescriptor<>( "Correlation Test Selector", CorrelationTest.class); static { diff --git a/src/main/java/pulse/tasks/Calculation.java b/src/main/java/pulse/tasks/Calculation.java index 7943c11..af33470 100644 --- a/src/main/java/pulse/tasks/Calculation.java +++ b/src/main/java/pulse/tasks/Calculation.java @@ -160,10 +160,11 @@ public void setScheme(DifferenceScheme scheme, ExperimentalData curve) { @SuppressWarnings({"unchecked", "rawtypes"}) public void process() throws SolverException { var list = problem.getProperties().findMalformedProperties(); - if(!list.isEmpty()) { + if (!list.isEmpty()) { StringBuilder sb = new StringBuilder("Illegal values:"); - for(NumericProperty np : list) - sb.append(String.format("%n %-25s", np)); + list.forEach(np + -> sb.append(String.format("%n %-25s", np)) + ); throw new SolverException(sb.toString()); } ((Solver) scheme).solve(problem); @@ -208,9 +209,10 @@ public boolean setStatus(Status status) { default: } - if(changeStatus) + if (changeStatus) { this.status = status; - + } + return changeStatus; } diff --git a/src/main/java/pulse/tasks/SearchTask.java b/src/main/java/pulse/tasks/SearchTask.java index 717b7bc..b17a6db 100644 --- a/src/main/java/pulse/tasks/SearchTask.java +++ b/src/main/java/pulse/tasks/SearchTask.java @@ -21,7 +21,6 @@ import static pulse.tasks.logs.Status.INCOMPLETE; import static pulse.tasks.logs.Status.IN_PROGRESS; import static pulse.tasks.logs.Status.READY; -import static pulse.tasks.logs.Status.TERMINATED; import static pulse.tasks.processing.Buffer.getSize; import static pulse.util.Reflexive.instantiate; @@ -31,8 +30,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.Logger; import java.util.stream.Collectors; import pulse.input.ExperimentalData; @@ -51,7 +48,6 @@ import pulse.tasks.logs.CorrelationLogEntry; import pulse.tasks.logs.DataLogEntry; import pulse.tasks.logs.Details; -import static pulse.tasks.logs.Details.SOLVER_ERROR; import pulse.tasks.logs.Log; import pulse.tasks.logs.LogEntry; import pulse.tasks.logs.StateEntry; @@ -59,6 +55,8 @@ import pulse.tasks.processing.Buffer; import pulse.tasks.processing.CorrelationBuffer; import pulse.util.Accessible; +import static pulse.tasks.logs.Status.AWAITING_TERMINATION; +import static pulse.tasks.logs.Status.TERMINATED; /** * A {@code SearchTask} is the most important class in {@code PULsE}. It @@ -81,7 +79,7 @@ public class SearchTask extends Accessible implements Runnable { private Buffer buffer; private Log log; - private CorrelationBuffer correlationBuffer; + private final CorrelationBuffer correlationBuffer; private CorrelationTest correlationTest; private NormalityTest normalityTest; @@ -171,7 +169,7 @@ private void addListeners() { *

*/ public void clear() { - stored = new ArrayList(); + stored = new ArrayList<>(); curve.resetRanges(); buffer = new Buffer(); correlationBuffer.clear(); @@ -233,15 +231,12 @@ public ParameterVector searchVector() { * * @param searchParameters an {@code IndexedVector} with relevant search * parameters + * @throws pulse.problem.schemes.solvers.SolverException * @see pulse.problem.statements.Problem.assign(IndexedVector) */ - public void assign(ParameterVector searchParameters) { - try { - current.getProblem().assign(searchParameters); - curve.getRange().assign(searchParameters); - } catch (SolverException e) { - notifyFailedStatus(e); - } + public void assign(ParameterVector searchParameters) throws SolverException { + current.getProblem().assign(searchParameters); + curve.getRange().assign(searchParameters); } /** @@ -284,8 +279,7 @@ public void run() { correlationBuffer.clear(); /* search cycle */ - - /* sets an independent thread for manipulating the buffer */ + /* sets an independent thread for manipulating the buffer */ List> bufferFutures = new ArrayList<>(bufferSize); var singleThreadExecutor = Executors.newSingleThreadExecutor(); @@ -295,8 +289,6 @@ public void run() { notifyFailedStatus(e1); } - final int maxIterations = (int) getInstance().getMaxIterations().getValue(); - outer: do { @@ -304,14 +296,8 @@ public void run() { for (var i = 0; i < bufferSize; i++) { - if (current.getStatus() != IN_PROGRESS) { - break outer; - } - - int iter = 0; - try { - for (boolean finished = false; !finished && iter < maxIterations; iter++) { + for (boolean finished = false; !finished;) { finished = optimiser.iteration(this); } } catch (SolverException e) { @@ -319,18 +305,12 @@ public void run() { break outer; } - if (iter >= maxIterations) { - var fail = FAILED; - fail.setDetails(MAX_ITERATIONS_REACHED); - setStatus(fail); - } - //if global best is better than the converged value if (best != null && best.getCost() < path.getCost()) { - //assign the global best parameters - assign(path.getParameters()); - //and try to re-calculate try { + //assign the global best parameters + assign(path.getParameters()); + //and try to re-calculate solveProblemAndCalculateCost(); } catch (SolverException ex) { notifyFailedStatus(ex); @@ -338,7 +318,7 @@ public void run() { } final var j = i; - + bufferFutures.add(CompletableFuture.runAsync(() -> { buffer.fill(this, j); correlationBuffer.inflate(this); @@ -349,13 +329,14 @@ public void run() { bufferFutures.forEach(future -> future.join()); - } while (buffer.isErrorTooHigh(errorTolerance)); + } while (buffer.isErrorTooHigh(errorTolerance) + && current.getStatus() == IN_PROGRESS); singleThreadExecutor.shutdown(); if (current.getStatus() == IN_PROGRESS) { runChecks(); - } + } } @@ -393,13 +374,13 @@ private void runChecks() { } } - } - - private void notifyFailedStatus(SolverException e1) { + + public void notifyFailedStatus(SolverException e1) { var status = Status.FAILED; status.setDetails(Details.SOLVER_ERROR); status.setDetailedMessage(e1.getMessage()); + e1.printStackTrace(); setStatus(status); } @@ -544,9 +525,9 @@ public String describe() { sb.append("_Task_"); var extId = curve.getMetadata().getExternalID(); if (extId < 0) { - sb.append("IntID_" + identifier.getValue()); + sb.append("IntID_").append(identifier.getValue()); } else { - sb.append("ExtID_" + extId); + sb.append("ExtID_").append(extId); } return sb.toString(); @@ -564,10 +545,9 @@ public void terminate() { case IN_PROGRESS: case QUEUED: case READY: - setStatus(TERMINATED); + setStatus(AWAITING_TERMINATION); break; default: - return; } } diff --git a/src/main/java/pulse/tasks/TaskManager.java b/src/main/java/pulse/tasks/TaskManager.java index 03aa334..f8801c2 100644 --- a/src/main/java/pulse/tasks/TaskManager.java +++ b/src/main/java/pulse/tasks/TaskManager.java @@ -26,11 +26,11 @@ import java.util.concurrent.Executors; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; -import javax.swing.SwingUtilities; import pulse.input.ExperimentalData; +import pulse.input.listeners.DataEvent; +import pulse.input.listeners.DataEventType; import pulse.properties.SampleName; import pulse.search.direction.PathOptimiser; @@ -39,6 +39,7 @@ import pulse.tasks.listeners.TaskSelectionEvent; import pulse.tasks.listeners.TaskSelectionListener; import pulse.tasks.logs.Status; +import static pulse.tasks.logs.Status.AWAITING_TERMINATION; import pulse.tasks.processing.Result; import pulse.tasks.processing.ResultFormat; import pulse.util.Group; @@ -58,19 +59,19 @@ */ public class TaskManager extends UpwardsNavigable { - private static TaskManager instance = new TaskManager(); + private static final TaskManager instance = new TaskManager(); private List tasks; private SearchTask selectedTask; private boolean singleStatement = true; - private ForkJoinPool taskPool; + private final ForkJoinPool taskPool; - private List selectionListeners; - private List taskRepositoryListeners; + private final List selectionListeners; + private final List taskRepositoryListeners; - private final static String DEFAULT_NAME = "Project 1 - " + now().format(ISO_WEEK_DATE); + private final static String DEFAULT_NAME = "Measurement_" + now().format(ISO_WEEK_DATE); private final HierarchyListener statementListener = e -> { @@ -96,10 +97,11 @@ private TaskManager() { addHierarchyListener(statementListener); /* Calculate the half-time once data is loaded. - */ + */ addTaskRepositoryListener((TaskRepositoryEvent e) -> { - if(e.getState() == TaskRepositoryEvent.State.TASK_ADDED) + if (e.getState() == TaskRepositoryEvent.State.TASK_ADDED) { getTask(e.getId()).getExperimentalCurve().calculateHalfTime(); + } }); } @@ -125,10 +127,10 @@ public void execute(SearchTask t) { //try to start cmputation // notify listeners computation is about to start - - if( ! t.setStatus(QUEUED) ) - return; - + if (!t.setStatus(QUEUED)) { + return; + } + // notify listeners calculation started notifyListeners(new TaskRepositoryEvent(TASK_SUBMITTED, t.getIdentifier())); @@ -141,9 +143,13 @@ public void execute(SearchTask t) { //notify listeners before the task is re-assigned notifyListeners(e); t.storeCalculation(); + } + else if(current.getStatus() == AWAITING_TERMINATION) { + t.setStatus(Status.TERMINATED); } - else + else { notifyListeners(e); + } }); } @@ -312,12 +318,12 @@ public SearchTask getTask(int externalId) { * Generates a {@code SearchTask} assuming that the {@code ExperimentalData} * is stored in the {@code file}. This will make the {@code ReaderManager} * attempt to read that {@code file}. If successful, invokes - * {@code addTask(...)} on the created {@code SearchTask}. After the - * task is generated, checks whether the acquisition time recorded by the experimental setup - * has been chosen appropriately. + * {@code addTask(...)} on the created {@code SearchTask}. After the task is + * generated, checks whether the acquisition time recorded by the + * experimental setup has been chosen appropriately. * * @see pulse.input.ExperimentalData.isAcquisitionTimeSensible() - + * *

* * @param file the file to load the experimental data from @@ -325,17 +331,24 @@ public SearchTask getTask(int externalId) { * @see pulse.io.readers.ReaderManager.extract(File) */ public void generateTask(File file) { - read(curveReaders(), file).stream().forEach((ExperimentalData curve) -> { + var curves = read(curveReaders(), file); + //notify curves have been loaded + curves.stream().forEach(c -> c.fireDataChanged(new DataEvent( + DataEventType.DATA_LOADED, c + ))); + //create tasks + curves.stream().forEach((ExperimentalData curve) -> { var task = new SearchTask(curve); addTask(task); var data = task.getExperimentalCurve(); - if(!data.isAcquisitionTimeSensible()) + if (!data.isAcquisitionTimeSensible()) { data.truncate(); + } }); } /** - * Generates multiple tasks from multiple {@code files}. + * Generates multiple tasks from multiple {@code files}. * * @param files a list of {@code File}s that can be parsed down to * {@code ExperimentalData}. @@ -344,22 +357,22 @@ 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(); + Runnable loader = () -> { + var pool = Executors.newSingleThreadExecutor(); files.stream().forEach(f -> pool.submit(() -> generateTask(f))); pool.shutdown(); - + try { pool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException ex) { Logger.getLogger(TaskManager.class.getName()).log(Level.SEVERE, null, ex); - } - + } + //when pool has been shutdown selectFirstTask(); - + }; - + Executors.newSingleThreadExecutor().submit(loader); } @@ -488,10 +501,13 @@ public List getTaskRepositoryListeners() { /** * This {@code TaskManager} will be described by the sample name for the * experiment. + * + * @return the string descriptor */ @Override public String describe() { - return tasks.size() > 0 ? getSampleName().toString() : DEFAULT_NAME; + var name = getSampleName(); + return name == null || name.getValue() == null ? DEFAULT_NAME : name.toString(); } public void evaluate() { diff --git a/src/main/java/pulse/tasks/logs/Status.java b/src/main/java/pulse/tasks/logs/Status.java index 080f6ed..87e226d 100644 --- a/src/main/java/pulse/tasks/logs/Status.java +++ b/src/main/java/pulse/tasks/logs/Status.java @@ -31,9 +31,16 @@ public enum Status { */ EXECUTION_ERROR(Color.red), /** - * The task has been terminated by the user. + * Termination requested. */ + AWAITING_TERMINATION(Color.DARK_GRAY), + + /** + * Task terminated + */ + TERMINATED(Color.DARK_GRAY), + /** * Task has been queued and is waiting to be executed. */ diff --git a/src/main/java/pulse/tasks/processing/ResultFormat.java b/src/main/java/pulse/tasks/processing/ResultFormat.java index 63c7679..6611a84 100644 --- a/src/main/java/pulse/tasks/processing/ResultFormat.java +++ b/src/main/java/pulse/tasks/processing/ResultFormat.java @@ -45,16 +45,11 @@ private ResultFormat() { private ResultFormat(List keys) { nameMap = new ArrayList<>(); - for (var key : keys) { - nameMap.add(key); - } - } - - private ResultFormat(ResultFormat fmt) { - nameMap = new ArrayList<>(fmt.nameMap.size()); - nameMap.addAll(fmt.nameMap); + keys.forEach(key -> + nameMap.add(key) + ); } - + public static void addResultFormatListener(ResultFormatListener rfl) { listeners.add(rfl); } diff --git a/src/main/java/pulse/ui/Launcher.java b/src/main/java/pulse/ui/Launcher.java index 6843a20..acadc0b 100644 --- a/src/main/java/pulse/ui/Launcher.java +++ b/src/main/java/pulse/ui/Launcher.java @@ -36,7 +36,7 @@ public class Launcher { private PrintStream errStream; private File errorLog; - private final static boolean DEBUG = false; + private final static boolean DEBUG = true; private static final File LOCK = new File("pulse.lock"); @@ -147,6 +147,10 @@ private void createShutdownHook() { if (errorLog != null && errorLog.exists() && errorLog.length() < 1) { errorLog.delete(); } + //delete lock explicitly on abnormal termination + if(LOCK.exists()) { + LOCK.delete(); + } }; Runtime.getRuntime().addShutdownHook(new Thread(r)); diff --git a/src/main/java/pulse/ui/components/CalculationTable.java b/src/main/java/pulse/ui/components/CalculationTable.java index 42c9c9b..9d966b4 100644 --- a/src/main/java/pulse/ui/components/CalculationTable.java +++ b/src/main/java/pulse/ui/components/CalculationTable.java @@ -81,8 +81,10 @@ public void initListeners() { var task = TaskManager.getManagerInstance().getSelectedTask(); var id = convertRowIndexToModel(this.getSelectedRow()); if (!lsm.getValueIsAdjusting() && id > -1 && id < task.getStoredCalculations().size()) { + task.switchTo(task.getStoredCalculations().get(id)); getChart().plot(task, true); + } }); diff --git a/src/main/java/pulse/ui/components/Chart.java b/src/main/java/pulse/ui/components/Chart.java index 2158758..753b878 100644 --- a/src/main/java/pulse/ui/components/Chart.java +++ b/src/main/java/pulse/ui/components/Chart.java @@ -217,8 +217,6 @@ private void adjustAxisLabel(double maximum) { public void plot(SearchTask task, boolean extendedCurve) { requireNonNull(task); - var plot = chart.getXYPlot(); - for (int i = 0; i < 6; i++) { plot.setDataset(i, null); } @@ -261,7 +259,7 @@ public void plot(SearchTask task, boolean extendedCurve) { var solution = problem.getHeatingCurve(); var scheme = calc.getScheme(); - if (solution != null && scheme != null && !solution.isIncomplete()) { + if (solution != null && scheme != null) { var solutionDataset = new XYSeriesCollection(); var displayedCurve = extendedCurve ? solution.extendedTo(rawData, problem.getBaseline()) : solution; diff --git a/src/main/java/pulse/ui/components/DataLoader.java b/src/main/java/pulse/ui/components/DataLoader.java index ec13f1b..d6b6539 100644 --- a/src/main/java/pulse/ui/components/DataLoader.java +++ b/src/main/java/pulse/ui/components/DataLoader.java @@ -21,7 +21,6 @@ import pulse.io.readers.MetaFilePopulator; import pulse.io.readers.ReaderManager; import pulse.problem.laser.NumericPulse; -import pulse.problem.laser.NumericPulseData; import pulse.tasks.SearchTask; import pulse.tasks.TaskManager; import pulse.tasks.listeners.TaskRepositoryEvent; @@ -66,6 +65,7 @@ public static void loadDataDialog() { var instance = TaskManager.getManagerInstance(); if (files != null) { + progressFrame.trackProgress(files.size()); instance.generateTasks(files); } @@ -151,13 +151,15 @@ public static void loadPulseDialog() { metadata.getPulseDescriptor() .setSelectedDescriptor( NumericPulse.class.getSimpleName()); - }); + progressFrame.incrementProgress(); + } + ); } }); }; - + Executors.newSingleThreadExecutor().submit(loader); } diff --git a/src/main/java/pulse/ui/components/PulseChart.java b/src/main/java/pulse/ui/components/PulseChart.java index 418ec70..bb33646 100644 --- a/src/main/java/pulse/ui/components/PulseChart.java +++ b/src/main/java/pulse/ui/components/PulseChart.java @@ -22,10 +22,10 @@ import pulse.problem.statements.Problem; import pulse.problem.statements.Pulse; +import pulse.tasks.Calculation; -public class PulseChart extends AuxPlotter { +public class PulseChart extends AuxPlotter { - private final static int NUM_PULSE_POINTS = 600; private final static double TO_MILLIS = 1E3; public PulseChart(String xLabel, String yLabel) { @@ -59,36 +59,33 @@ private void setLegendTitle() { } @Override - public void plot(Pulse pulse) { - requireNonNull(pulse); + public void plot(Calculation c) { + requireNonNull(c); + + Problem problem = c.getProblem(); - var problem = (Problem) pulse.getParent(); double startTime = (double) problem.getHeatingCurve().getTimeShift().getValue(); var pulseDataset = new XYSeriesCollection(); - pulseDataset.addSeries(series(problem, startTime)); + + pulseDataset.addSeries(series(problem.getPulse(), c.getScheme().getGrid().getTimeStep(), + problem.getProperties().timeFactor(), startTime)); getPlot().setDataset(0, pulseDataset); } - private static XYSeries series(Problem problem, double startTime) { - var pulse = problem.getPulse(); - + private static XYSeries series(Pulse pulse, double dx, double timeFactor, double startTime) { var series = new XYSeries(pulse.getPulseShape().toString()); - - double timeLimit = (double) pulse.getPulseWidth().getValue(); - final double timeFactor = problem.getProperties().timeFactor(); - - double dx = timeLimit / (NUM_PULSE_POINTS - 1); - double x = startTime; - - series.add(TO_MILLIS * (startTime - dx / 10.), 0.0); - series.add(TO_MILLIS * (startTime + timeLimit + dx / 10.), 0.0); - var pulseShape = pulse.getPulseShape(); + + double timeLimit = pulseShape.getPulseWidth(); + 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); - for (var i = 0; i < NUM_PULSE_POINTS; i++) { - series.add(x * 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/ResultTable.java b/src/main/java/pulse/ui/components/ResultTable.java index 141d0f4..520aa0e 100644 --- a/src/main/java/pulse/ui/components/ResultTable.java +++ b/src/main/java/pulse/ui/components/ResultTable.java @@ -178,7 +178,7 @@ public String getToolTipText(MouseEvent e) { @Override public String describe() { - return "SummaryTable"; + return "Summary_" + TaskManager.getManagerInstance().describe(); } public boolean isSelectionEmpty() { diff --git a/src/main/java/pulse/ui/components/buttons/LoaderButton.java b/src/main/java/pulse/ui/components/buttons/LoaderButton.java index 5b93f01..46c9b43 100644 --- a/src/main/java/pulse/ui/components/buttons/LoaderButton.java +++ b/src/main/java/pulse/ui/components/buttons/LoaderButton.java @@ -24,8 +24,10 @@ import javax.swing.JFileChooser; import javax.swing.UIManager; import javax.swing.filechooser.FileNameExtensionFilter; +import org.apache.commons.math3.exception.OutOfRangeException; import pulse.input.InterpolationDataset; +import pulse.ui.Messages; import pulse.util.ImageUtils; @SuppressWarnings("serial") @@ -84,7 +86,18 @@ public void init() { showMessageDialog(getWindowAncestor((Component) arg0.getSource()), getString("LoaderButton.ReadError"), //$NON-NLS-1$ getString("LoaderButton.IOError"), //$NON-NLS-1$ ERROR_MESSAGE); - e.printStackTrace(); + } + catch(OutOfRangeException ofre) { + getDefaultToolkit().beep(); + StringBuilder sb = new StringBuilder(getString("TextWrap.0")); + 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(), + getString("LoaderButton.OFRError"), //$NON-NLS-1$ + ERROR_MESSAGE); } var size = getDataset(dataType).getData().size(); var label = ""; diff --git a/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java b/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java index 2c5d4fd..ac0c3bd 100644 --- a/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java +++ b/src/main/java/pulse/ui/components/controllers/InstanceCellEditor.java @@ -27,9 +27,14 @@ public Component getTableCellEditorComponent(JTable table, Object value, boolean combobox = new JComboBox<>(((InstanceDescriptor) value).getAllDescriptors().toArray()); combobox.setSelectedItem(descriptor.getValue()); - combobox.addItemListener(e -> { + combobox.addItemListener((ItemEvent e) -> { if (e.getStateChange() == ItemEvent.SELECTED) { - descriptor.attemptUpdate(e.getItem()); + try { + descriptor.attemptUpdate(e.getItem()); + } catch(NullPointerException npe) { + System.out.println("Error updating " + descriptor.getDescriptor(false) + + ". Cannot be set to " + e.getItem()); + } } }); diff --git a/src/main/java/pulse/ui/components/models/ResultTableModel.java b/src/main/java/pulse/ui/components/models/ResultTableModel.java index 5910a00..ffd35db 100644 --- a/src/main/java/pulse/ui/components/models/ResultTableModel.java +++ b/src/main/java/pulse/ui/components/models/ResultTableModel.java @@ -135,7 +135,8 @@ public void merge(double temperatureDelta) { avgResults.addAll(group); } else { //add and average result - avgResults.add(new AverageResult(group, fmt)); + var result = new AverageResult(group, fmt); + avgResults.add(result); } //ignore processed results later on @@ -218,47 +219,53 @@ private List tooltips() { public void addRow(AbstractResult result) { Objects.requireNonNull(result, "Entry added to the results table must not be null"); - //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; - - //any old result asssociated withis this task - var oldResult = results.stream().filter(r - -> r.specificAncestor( - SearchTask.class) == parentTask).findAny(); - //ignore average results - if (result instanceof Result && oldResult.isPresent()) { - AbstractResult oldResultExisting = oldResult.get(); - Optional oldCalculation = parentTask.getStoredCalculations().stream() - .filter(c -> c.getResult().equals(oldResultExisting)).findAny(); - - //old calculation found - if (oldCalculation.isPresent()) { - - //since the task has already been completed anyway - Status status = Status.DONE; + 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; + + //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(); + Optional oldCalculation = parentTask.getStoredCalculations().stream() + .filter(c -> c.getResult().equals(oldResultExisting)).findAny(); + + //old calculation found + if (oldCalculation.isPresent()) { + + //since the task has already been completed anyway + Status status = Status.DONE; + + //better result than already present -- update table + if (parentTask.getCurrentCalculation().isBetterThan(oldCalculation.get())) { + remove(oldResultExisting); + status.setDetails(Details.BETTER_CALCULATION_RESULTS_THAN_PREVIOUSLY_OBTAINED); + parentTask.setStatus(status); + } else { + //do not remove result and do not add new result + status.setDetails(Details.CALCULATION_RESULTS_WORSE_THAN_PREVIOUSLY_OBTAINED); + parentTask.setStatus(status); + return; + } - //better result than already present -- update table - if (parentTask.getCurrentCalculation().isBetterThan(oldCalculation.get())) { - remove(oldResultExisting); - status.setDetails(Details.BETTER_CALCULATION_RESULTS_THAN_PREVIOUSLY_OBTAINED); - parentTask.setStatus(status); } else { - //do not remove result and do not add new result - status.setDetails(Details.CALCULATION_RESULTS_WORSE_THAN_PREVIOUSLY_OBTAINED); - parentTask.setStatus(status); - return; - } + //calculation has been purged -- delete previous result - } else { - //calculation has been purged -- delete previous result + remove(oldResultExisting); - remove(oldResultExisting); + } } diff --git a/src/main/java/pulse/ui/components/models/SelectedKeysModel.java b/src/main/java/pulse/ui/components/models/SelectedKeysModel.java index 3234193..98491bf 100644 --- a/src/main/java/pulse/ui/components/models/SelectedKeysModel.java +++ b/src/main/java/pulse/ui/components/models/SelectedKeysModel.java @@ -16,7 +16,7 @@ public class SelectedKeysModel extends DefaultTableModel { * */ private static final long serialVersionUID = 1L; - private List elements; + private final List elements; private final List referenceList; private final NumericPropertyKeyword[] mandatorySelection; @@ -28,7 +28,7 @@ public SelectedKeysModel(List keys, NumericPropertyKeywo update(); } - public void update() { + public final void update() { update(referenceList); } diff --git a/src/main/java/pulse/ui/components/panels/ProblemToolbar.java b/src/main/java/pulse/ui/components/panels/ProblemToolbar.java index aa85aed..0aeedc8 100644 --- a/src/main/java/pulse/ui/components/panels/ProblemToolbar.java +++ b/src/main/java/pulse/ui/components/panels/ProblemToolbar.java @@ -49,8 +49,7 @@ public ProblemToolbar() { add(btnLoadDensity); btnSimulate.addActionListener((ActionEvent e) - -> Executors.newSingleThreadExecutor().submit(() - -> ProblemToolbar.plot(e))); + -> plot(e)); } public static void plot(ActionEvent e) { @@ -76,13 +75,14 @@ public static void plot(ActionEvent e) { } else { try { - ((Solver) calc.getScheme()).solve(calc.getProblem()); + Solver solver = (Solver) calc.getScheme(); + solver.solve(calc.getProblem()); } catch (SolverException se) { err.println("Solver of " + t + " has encountered an error. Details: "); se.printStackTrace(); } MainGraphFrame.getInstance().plot(); - TaskControlFrame.getInstance().getPulseFrame().plot(calc.getProblem().getPulse()); + TaskControlFrame.getInstance().getPulseFrame().plot(calc); } } diff --git a/src/main/java/pulse/ui/frames/TaskControlFrame.java b/src/main/java/pulse/ui/frames/TaskControlFrame.java index 8a32dc3..3bcccea 100644 --- a/src/main/java/pulse/ui/frames/TaskControlFrame.java +++ b/src/main/java/pulse/ui/frames/TaskControlFrame.java @@ -26,6 +26,7 @@ import javax.swing.event.InternalFrameEvent; import pulse.problem.statements.Pulse; +import pulse.tasks.Calculation; import pulse.tasks.TaskManager; import pulse.ui.Version; import pulse.ui.components.PulseChart; @@ -51,7 +52,7 @@ public class TaskControlFrame extends JFrame { private ResultFrame resultsFrame; private MainGraphFrame graphFrame; private LogFrame logFrame; - private InternalGraphFrame pulseFrame; + private InternalGraphFrame pulseFrame; private PulseMainMenu mainMenu; @@ -201,7 +202,7 @@ private void initComponents() { searchOptionsFrame = new SearchOptionsFrame(); searchOptionsFrame.setFrameIcon(loadIcon("optimiser.png", 20, Color.white)); - pulseFrame = new InternalGraphFrame("Pulse Shape", new PulseChart("Time (ms)", "Laser Power (a. u.)")); + pulseFrame = new InternalGraphFrame("Pulse Shape", new PulseChart("Time (ms)", "Laser Power (a. u.)")); pulseFrame.setFrameIcon(loadIcon("pulse.png", 20, Color.white)); pulseFrame.setVisible(false); @@ -453,7 +454,7 @@ public Mode getMode() { return mode; } - public InternalGraphFrame getPulseFrame() { + public InternalGraphFrame getPulseFrame() { return pulseFrame; } diff --git a/src/main/java/pulse/ui/frames/dialogs/ResultChangeDialog.java b/src/main/java/pulse/ui/frames/dialogs/ResultChangeDialog.java index ee1c7d9..b0554c3 100644 --- a/src/main/java/pulse/ui/frames/dialogs/ResultChangeDialog.java +++ b/src/main/java/pulse/ui/frames/dialogs/ResultChangeDialog.java @@ -3,19 +3,11 @@ import static javax.swing.SwingConstants.BOTTOM; import java.awt.BorderLayout; -import java.awt.Component; import javax.swing.JDialog; -import javax.swing.JTable; -import javax.swing.JTextArea; import javax.swing.SwingConstants; -import static javax.swing.SwingConstants.SOUTH; -import static javax.swing.SwingConstants.TOP; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableCellRenderer; import pulse.tasks.processing.ResultFormat; -import pulse.ui.Messages; import pulse.ui.components.models.ParameterTableModel; import pulse.ui.components.models.SelectedKeysModel; import pulse.ui.components.panels.DoubleTablePanel; @@ -30,7 +22,6 @@ public class ResultChangeDialog extends JDialog { private final static int HEIGHT = 600; public ResultChangeDialog() { - setTitle("Result output formatting"); initComponents(); setSize(WIDTH, HEIGHT); @@ -47,8 +38,6 @@ public void setVisible(boolean value) { } private void initComponents() { - java.awt.GridBagConstraints gridBagConstraints; - MainToolbar = new javax.swing.JToolBar(); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0)); diff --git a/src/main/java/pulse/util/PropertyHolder.java b/src/main/java/pulse/util/PropertyHolder.java index 8238a33..05cef73 100644 --- a/src/main/java/pulse/util/PropertyHolder.java +++ b/src/main/java/pulse/util/PropertyHolder.java @@ -289,6 +289,7 @@ public String getPrefix() { * * @return the descriptor */ + @Override public String getDescriptor() { return prefix != null ? getPrefix() : super.getDescriptor(); } diff --git a/src/main/resources/NumericProperty.xml b/src/main/resources/NumericProperty.xml index 3e3180a..3d4b61a 100644 --- a/src/main/resources/NumericProperty.xml +++ b/src/main/resources/NumericProperty.xml @@ -180,7 +180,7 @@ visible="true" descriptor="Optical thickness, <i>&tau;</i><sub>0</sub>" dimensionfactor="1" discreet="false" keyword="OPTICAL_THICKNESS" - maximum="100000" minimum="1e-4" primitive-type="double" value="0.1" + maximum="100000" minimum="1e-3" primitive-type="double" value="0.1" default-search-variable="true">
@@ -301,9 +301,9 @@ dimensionfactor="1000.0" keyword="DIAMETER" maximum="0.1" minimum="1.0E-6" value="0.01" primitive-type="double" discreet="true"/> @@ -351,7 +351,7 @@ abbreviation="<i>d</i><sub>las</sub> (mm)" visible="true" descriptor="Laser spot diameter, <i>d</i><sub>las</sub> (mm)" - dimensionfactor="1000.0" keyword="SPOT_DIAMETER" maximum="0.05" + dimensionfactor="1000.0" keyword="SPOT_DIAMETER" maximum="0.2" minimum="1.0E-4" value="0.01" primitive-type="double" discreet="true"> HEAT_LOSS_COMBINED + + Classical 1D Problem Statement
  • Linearized heat losses (front and rear)
  • One-dimensional heat flow
  • Dimensionless formulation
  • Cp and ρ assumed constant
DistributedProblem.Descriptor=Penetration (1D) Problem Statement
  • Based on 1D formulation, except:
  • Distributed radiation absorption (finite penetration depth) is considered
  • Detector measures an integral signal from the bulk of the sample
  • Laser and thermal radiation absorption are considered separately
-ParticipatingMedium.Descriptor=Participating Medium (1D) Problem Statement
  • Based on a coupled radiative-conductive heat transfer model with distributed absorption, emission, and scattering;
  • Describes a sample with opaque coatings on front and rear faces and a semi-transparent bulk;
  • Sample material acts a continuous medium in terms of absorption, emission, and scattering.
+ParticipatingMedium.Descriptor=Participating Medium (1D) Problem Statement
  • Based on a coupled radiative-conductive heat transfer model with distributed absorption, emission, and scattering;
  • Describes a sample with opaque coatings on front and rear faces and a semi-transparent bulk;
  • Sample material acts a continuous medium in terms of absorption, emission, and scattering.
  • Allows a nonlinear emission function -- hence requires Cp and ρ data
+LinearisedParticipatingMedium.Descriptor=Linearised Participating Medium (1D) Problem Statement
  • Based on a coupled radiative-conductive heat transfer model with distributed absorption, emission, and scattering;
  • Describes a sample with opaque coatings on front and rear faces and a semi-transparent bulk;
  • Sample material acts a continuous medium in terms of absorption, emission, and scattering.
  • Assumes heating is small to suppress nonlinear emission
DiathermicProblem.Descriptor=Diathermic Sample with Grey Walls (1D) Problem Statement
  • Based on 1D formulation, except:
  • Laser radiation is absorbed at the front surface and then re-radiated to the rear surface in the form of thermal radiation
  • Sample material considered fully transparent to any kind of radiation
LinearizedProblem2D.Descriptor=Classical 2D Problem Statement
  • Based on 1D formulation, except:
  • Allows heat losses from side surface
  • Allows radial heat flow
UniformlyCoatedSample.Descriptor=Core-Shell 2D Problem Statement
  • Based on the classical 2D problem, except:
  • Explicitly accounts for a coating that covers front, rear, and side surfaces
  • Allows for axial, radial, and circumferential heat fluxes
@@ -91,6 +92,9 @@ LoaderButton.5=Specific heat, (CP) LoaderButton.6=Density, (ρ) LoaderButton.IOError=I/O Error LoaderButton.ReadError=Unable to read data from file\! +LoaderButton.OFRError=Out of Range +LoaderButton.OFRErrorDescriptor=Data file does not cover the test temperature of current measurement: +LoaderButton.OFRErrorDescriptor2=. Please try expanding the temperature range in the input file! LoaderButton.SupportedExtensionsDescriptor=Supported thermal properties files LogPane.Init=Initializing... LogPane.InsertError=Could not insert log entry to log panel @@ -273,13 +277,16 @@ complexity.warning=

You have selected a high ExplicitScheme.2=Time interval too small: ExplicitScheme.3=Problem not supported or unknown: ExplicitScheme.4=Forward Time, Centred Space (FTCS) Scheme

  • Order of approximation O(h2 + τ)
  • Conditionally stable
  • Faster than other schemes
+ExplicitScheme.5=Forward Time, Centred Space (FTCS) Scheme (NL)
  • Order of approximation O(h2 + τ)
  • Conditionally stable
  • Faster than other schemes
ImplicitScheme.2=Time interval too small: ImplicitScheme.3=Problem not supported or unknown: ImplicitScheme.4=Fully Implicit Scheme
  • Order of approximation O(h2 + &tau)
  • Unconditionally stable
  • Intermediate computational cost
  • Finite-difference representation of boundary conditions uses a Taylor expansion with three terms
+ImplicitScheme.5=Fully Implicit Scheme (NL)
  • Order of approximation O(h2 + &tau)
  • Unconditionally stable
  • Intermediate computational cost
  • Heat equation and BC are linear while RTE has a nonlinear emission term processed with a fixed iteration algorithm
MixedScheme.2=Time interval too small: MixedScheme.3=Problem not supported or unknown: MixedScheme.4=Symmetric Semi-Implicit Scheme
  • Order of approximation O(h2 + &tau2)
  • Unconditionally stable
  • Steps are computationally more expensive but their number is fewer compared to other schemes
  • Finite-difference representation of boundary conditions uses a Taylor expansion with three terms
  • Weight is set to 0.5
MixedScheme2.4=Increased Accuracy Semi-implicit Scheme
  • Order of approximation O(h4 + &tau2)
  • Unconditionally stable
  • Steps are computationally more expensive but their number is fewer compared to other schemes
  • Finite-difference representation of boundary conditions uses a Taylor expansion with three terms
  • Auto-adjusts its weight and discrete representation of the flux derivative based on accuracy.
+MixedScheme2.5=Increased Accuracy Semi-implicit Scheme (NL)
  • Order of approximation O(h4 + &tau2)
  • Unconditionally stable
  • Steps are computationally more expensive but their number is fewer compared to other schemes
  • Heat equation and BC are linear while RTE has a nonlinear emission term processed with a fixed iteration algorithm
TextWrap.0=

TextWrap.1=

TextWrap.2=

\ No newline at end of file diff --git a/src/test/java/test/NonscatteringSetup.java b/src/test/java/test/NonscatteringSetup.java index d56f41c..5de9622 100644 --- a/src/test/java/test/NonscatteringSetup.java +++ b/src/test/java/test/NonscatteringSetup.java @@ -16,6 +16,7 @@ import pulse.problem.schemes.DifferenceScheme; import pulse.problem.schemes.rte.RadiativeTransferSolver; import pulse.problem.schemes.solvers.ImplicitCoupledSolver; +import pulse.problem.schemes.solvers.ImplicitCoupledSolverNL; import pulse.problem.statements.ParticipatingMedium; import pulse.problem.statements.Pulse2D; import pulse.problem.statements.model.ThermoOpticalProperties; @@ -38,7 +39,7 @@ public NonscatteringSetup(final int testProfileSize, final double maxHeating) { properties.setTestTemperature(derive(TEST_TEMPERATURE, 800.0)); properties.setScatteringAlbedo(derive(SCATTERING_ALBEDO, 0.0)); - testScheme = new ImplicitCoupledSolver(); + testScheme = new ImplicitCoupledSolverNL(); var grid = testScheme.getGrid(); grid.setGridDensity(derive(GRID_DENSITY, testProfileSize - 1));