From ac550160d44799c095d436ec166749d170cee14f Mon Sep 17 00:00:00 2001
From: kotik-coder
Date: Mon, 6 Jun 2022 20:26:00 +0300
Subject: [PATCH 1/3] Major Changes and Fixes
#LinearAnisotropicPF
- Changed reference from ParticipatingMedium to ThermoOpticalProperties
- function(...) : replaced calculation with cosineTheta(i,k) call
#CurveEventType
- Added CALCULATION_FINISHED event type
#DataEvent
- Replaced reference to ExperimentalData with a more general reference
to AbstractData
#NetzschPulseCSVReader
- Added locale support
#SampleName
- A default sample name of null is used, in order to avoid having too
many 'Nameless' calculations
#TridiagonalMatrixAlgorithm
- Instead of holding a reference to the Grid object, the fields tau,
N and h are now added
- As the instance of this class is created just before the calculation
starts, it does not need to reference Grid
#ResultTableModel
- Better handling of average results vs individual results
- Average result now ignored when checking for previously calculated
results of a task. This was done because an average result is not assigned
to a task, therefore, it returns null when searching for a SearchTask
ancestor. This led to a NullPointerException previously, but now has been
fixed.
#ProblemToolbar
- Replaced unnecessary SingleThreadExecturo with a direct call to the
plot (...) method
#DataLoader
- Added missing .incrementProgress() call when loading pulse data
#Problem
- Removed unnecessary check c.isIncomplete() for ExperimentalData when
applying baseline-
- Changed availableSolutions() to accomodate for possible multiple
application of a single solver
- Improved confusing assignments in the optimisationVector(...) method
- Fixed error: assign(...) -> changed .get(...) to inverseTransform(...)
- Consequently, made sure that this latter value is referenced by the
setter methods
- Removed shortName(...) method
- Removed conditional statement prior to applying baseline in
setBaseline(....)
#Discretisation
- Switched to ThermoOpticalProperties reference
#HeatingCurve
- Added lastCalculation(...) array to store last successful calculation
- Added copyToLastCalculation()
- apply(Baseline) now only works for a positive size of the time sequence
#ADILinearisedSolver
- Added an overriden clearArrays() method for array init purposes
- Removed array initialisation from prepare(...)
- prepare() now publicly overrides superclass method
#ImplicitScheme
- Removed unnecessary final int m argument from several methods
#MixedLinearisedSolver
- Changed scheme to accomodate the additional zeta parameter for
rear-side parasitic heating
- Added an overriding pulse(...) method
- Changed firstBeta(...)
#ImplicitLinearisedSolver
- Removed unnecessary call to super.pulse(...)
#General changes:
- Added zeta to 1D Classical, Diathermic and ParticipatingMedium problems.
Zeta serves to simulate a possible rear-surface heat source, either due
to beam deflection or circumferential conduction
#DiathermicMedium
- Bounds in optimisationVector(...) for the DIATHERMIC_COEFFICIENT have
been changed from arbitrary valueto those initially set in the xml document
- Removed constraint on the diathermic coefficient for curves having
pre-loaded property tables
#DiscreteOrdinatesMethod
- Replaced references to Problem with ThermoOpticalProperties where possible
#RadiativeTransferCoupling
- Added a safety check to init(...) to avoid potential handling of
unsupported problems
- Changed signature of newRTE method
#ExplicitCoupledSolver
- Added an overriding timeStep(...) method, which redirects to
explicitSolution(...)
- Changed finaliseStep() overriding method. If the fluxes are auto-updated,
computes the solution to the RTE. After that, stores the fluxes.
- Added autoUpdatesFluxes capability and the zeta factor
- Removed unnecessary RTE calculation before main sequence started
- Removed pls accessor, which is defined in the superclass. Instead,
replaced with a getCurrentPulse() method
#Chart
- Removed unnecessary call in the conditional statement of a for loop to
.isIncomplete()
#GradientGuidedOptimisation
- Replaced the body of the catch block in configure(...) by a call to
notifyFailedStatus() in the SearchTask
#ThermoOpticalProperties
- Made this class implement the Optimisable interface and moved all
property assignments from ParticipatingMedium to this class
#Pulse
- Fixed incorrect range updating after pulse changes when the lower
range would be shifted to a lower value automatically although a higher
value was set
#Metadata
- Added call to setPulseWidth after pulse data loaded
#OneDimensionalScheme
- Moved array initialisation to clearArrays() removing them from prepare()
#LoaderButton
- Added OutOfRangeException checks if thermal property table does not
cover a wide enough temperature range
#SearchTask
- Removed unnecessary catch clause in assign
- Removed wrong logic in relation to maxIterations assignment and the
inner iteration cycle
- Removed inefficient checks for IN_PROGRESS calculations within the
inner cycle
- Added distinction between AWAITING_TERMINATION and TERMINATED statuses
#DifferenceScheme
- Added pls field as a generic place holder for the current pulse value
- Added call to clearArrays() from inside the prepare(...) method
- Removed maxTemp/apparentMaximum scaling to enable Cp calculation in future
- Removed adjustment cycle from runTimeSequence. Prevented number of
points from being adjusted
- Added missing prepareStep() in the timeSegment() method
- Added the clearArrays() method
- Somewhat more logical structure of the runTimeSequence method
#ResultTable
- Added a more informative content to the describe() method so that it
actually describes the sample
#MixedCoupledSolver
- Replaced private prepare() with overriden public prepare()
- Added rear-surface heating to BCs
- removed unnecessary arguments from evalRightBoundary
#AbsorptionModel
- Error fixed: Remvoed premature setParameterBounds() call, which
overrides all bounds in the vector
#BlockMatrixAlgorithm
- Fixed problem with grid density not being assigned
#RectangularPulse
- Added a required number of points threshold of 4
#ImplicitTranslucentSolver
- Removed superfluous fields
- Replaced pls with reference to getCurrentPulse()
- Removed unnecessary timeStep() overriding
- Removed redundant parameter arguments
#ResultTableExporter
- Improved output format of the CSV summary tables, adding version,
date, and column identifiers
- Fixed an error with number of columns changing
- Changed the plus-minus delimiter to ;
#ExponentiallyModifiedGaussian
#TrapezoidalPulse
- Added required number of points (10)
- Remove redundant init() override
#DiscretePulse2D
- A new private init(...) method has been introduced.
- A new evalPulseSpot() method is introduced, which caclculates a non-zero
pulse spot in multiples of the grid radial step and automatically adjusts
the grid to allow a large enough step size
- A listener has now been added to the properties of the problem under
study, which triggers recalculation of the radial spot size
#DiscretePulse
- Added WIDTH_TOLERANCE_FACTOR, which determines the minimal allowed pulse
width for pulse corrections
- Added listener to Grid changes, requesting to re-init the DiscretePulse
- Added the init() method
- Pulse normalisation (area calculation) now done from within DiscretePulse
- recalculate(...) now checks whether the nominal width is feasible.
Otherwise adjusts the pulse shape and width to prevent calculations from
slowing down dramatically
- Upon setting the discreteWidth, the grid is re-adjusted to accomodate
these changes
#CoupledImplicitScheme
- Separated nonlinear BC treatment from the main logic
#ImplicitNonlinearSolver
#implicitCoupledSolver
#ExplicitCoupledSolver
- Replaced private prepare(...) method with a public overriden prepare(...)
with a Problem argument
#ClassicalProblem2D
- Fixed optimisationVector
#TaskManager
- Added support for awaiting termination / terminated statuses
- generateTask() now triggers DATA_LOADED events after the experimental
profiles have been loaded and before they are transferred to tasks
- describe() became slightly more informative
#ParticipatingMedium
- Bulk of optimisationVector() and assign() moved to ThermoOpticalProperties
#ImplicitDiathermicSolver
- Fixed possible error in BC equations
- Added the rear-surface heat source
- private prepare() to public prepare()
#InstanceCellEditor
- Added check for NullPointerException in case when a change is not
possible (e.g. setting a NumericPulse when no such data is available)
#NetzschCSVReader
- Better handling of locale-specific delimiters
- Added guessLocaleAndFormat() method
- Delimiters can now be changed multiple times during reading the same
document
#AbstractData
- Removed redundant isIncomplete() method
- Added isFull() helper method (note the usage is different from isIncomplete)
#NumericPulse
- Added required number of calculation points, equal to 20.
- init(...) has been simplified by splitting it up in smaller chunks of code
- pulse width is now unambigously defined via the doInterpolation(...)
method where it is set to the last element of the scaled time sequence
(dimensionless)
- As a consequence, adjustedPulseWidth field is no longer needed and
has been removed
#NumericPulseData
- Removed redundant scale()
#CompositePathOptimiser
- Removed redundant checks for malformed candidate parameters
#Grid2D
- adjustTo() -> adjustStepSize()
- replaced for(...) loop with a recursive call to adjustStepSize
- after step size changed, calls adjustTimeStep
#Grid
- adjustTo() -> adjustTimeStep()
- Exploits required number of points, which differs depending on pulse
shape. Checks if the nominal pulse width is greater than the resolved
width. Adjusts time factor
#Status
- Added AWAITING_TERMINATION and TERMINATED
#Added classes:
- CornetteSchanksPF represents a new type of a phase function selectable
for PaticipatingMedium calculations
- ImplicitCoupledSolverNL, ExplicitCoupledSolverNL and MixedCoupledSolverNL,
which extends over MixedCoupledSolver and adds functionality specifically
to deal with the nonlinear BC terms
#Classes removed:
- LayeredGrid2D
- Partition
- CoreShellProblem (no longer considered necessary after introducing zeta)
- ADILayeredSolver
Minor changes
---
pom.xml | 2 +-
src/main/java/pulse/AbstractData.java | 28 ++-
src/main/java/pulse/HeatingCurve.java | 56 +++---
src/main/java/pulse/HeatingCurveListener.java | 1 +
.../java/pulse/input/ExperimentalData.java | 25 +--
src/main/java/pulse/input/Metadata.java | 16 +-
.../pulse/input/listeners/CurveEventType.java | 9 +-
.../java/pulse/input/listeners/DataEvent.java | 8 +-
.../pulse/input/listeners/DataEventType.java | 8 +-
.../pulse/io/export/ResultTableExporter.java | 44 +++--
.../pulse/io/readers/NetzschCSVReader.java | 98 +++++++----
.../io/readers/NetzschPulseCSVReader.java | 5 +-
.../java/pulse/io/readers/ReaderManager.java | 4 +-
.../pulse/math/FixedIntervalIntegrator.java | 10 +-
src/main/java/pulse/math/ParameterVector.java | 1 -
.../math/transforms/InvLenSqTransform.java | 4 +-
.../pulse/math/transforms/StickTransform.java | 1 +
.../pulse/problem/laser/DiscretePulse.java | 137 ++++++++++++---
.../pulse/problem/laser/DiscretePulse2D.java | 36 ++--
.../laser/ExponentiallyModifiedGaussian.java | 29 +---
.../pulse/problem/laser/NumericPulse.java | 81 ++++-----
.../pulse/problem/laser/NumericPulseData.java | 21 +--
.../problem/laser/PulseTemporalShape.java | 50 +-----
.../pulse/problem/laser/RectangularPulse.java | 8 +
.../pulse/problem/laser/TrapezoidalPulse.java | 26 ++-
.../java/pulse/problem/schemes/ADIScheme.java | 11 ++
.../problem/schemes/BlockMatrixAlgorithm.java | 21 ++-
.../schemes/CoupledImplicitScheme.java | 95 ++++------
.../problem/schemes/DifferenceScheme.java | 151 ++++++++--------
.../problem/schemes/DistributedDetection.java | 2 +-
src/main/java/pulse/problem/schemes/Grid.java | 76 +++++---
.../java/pulse/problem/schemes/Grid2D.java | 37 ++--
.../pulse/problem/schemes/ImplicitScheme.java | 16 +-
.../pulse/problem/schemes/LayeredGrid2D.java | 85 ---------
.../problem/schemes/OneDimensionalScheme.java | 7 +-
.../java/pulse/problem/schemes/Partition.java | 66 -------
.../schemes/RadiativeTransferCoupling.java | 12 +-
.../schemes/TridiagonalMatrixAlgorithm.java | 43 +++--
.../schemes/rte/BlackbodySpectrum.java | 42 +++--
.../schemes/rte/RadiativeTransferSolver.java | 14 +-
.../schemes/rte/dom/CornetteSchanksPF.java | 42 +++++
.../rte/dom/DiscreteOrdinatesMethod.java | 39 +++--
.../schemes/rte/dom/DiscreteQuantities.java | 26 +--
.../schemes/rte/dom/Discretisation.java | 14 +-
.../schemes/rte/dom/ExplicitRungeKutta.java | 2 +-
.../schemes/rte/dom/HenyeyGreensteinPF.java | 13 +-
.../schemes/rte/dom/LinearAnisotropicPF.java | 12 +-
.../schemes/rte/dom/ODEIntegrator.java | 25 +--
.../problem/schemes/rte/dom/OrdinateSet.java | 4 +-
.../schemes/rte/dom/PhaseFunction.java | 20 ++-
.../NonscatteringAnalyticalDerivatives.java | 5 +-
.../exact/NonscatteringRadiativeTransfer.java | 10 +-
.../schemes/solvers/ADILayeredSolver.java | 89 ----------
.../schemes/solvers/ADILinearisedSolver.java | 33 ++--
.../solvers/ExplicitCoupledSolver.java | 92 +++++-----
.../solvers/ExplicitCoupledSolverNL.java | 95 ++++++++++
.../solvers/ExplicitLinearisedSolver.java | 8 +-
.../solvers/ExplicitNonlinearSolver.java | 11 +-
.../solvers/ExplicitTranslucentSolver.java | 17 +-
.../solvers/ImplicitCoupledSolver.java | 30 ++--
.../solvers/ImplicitCoupledSolverNL.java | 94 ++++++++++
.../solvers/ImplicitDiathermicSolver.java | 29 ++--
.../solvers/ImplicitLinearisedSolver.java | 19 +-
.../solvers/ImplicitNonlinearSolver.java | 20 +--
.../solvers/ImplicitTranslucentSolver.java | 49 ++----
.../schemes/solvers/MixedCoupledSolver.java | 42 ++---
.../schemes/solvers/MixedCoupledSolverNL.java | 92 ++++++++++
.../solvers/MixedLinearisedSolver.java | 30 ++--
.../statements/ClassicalProblem2D.java | 26 +--
.../problem/statements/CoreShellProblem.java | 164 ------------------
.../problem/statements/DiathermicMedium.java | 13 +-
.../problem/statements/NonlinearProblem.java | 2 +
.../statements/ParticipatingMedium.java | 80 ++-------
.../statements/PenetrationProblem.java | 13 +-
.../pulse/problem/statements/Problem.java | 89 +++++-----
.../java/pulse/problem/statements/Pulse.java | 38 ++--
.../statements/model/AbsorptionModel.java | 2 +-
.../model/ExtendedThermalProperties.java | 4 -
.../statements/model/ThermalProperties.java | 6 +-
.../model/ThermoOpticalProperties.java | 82 ++++++++-
.../java/pulse/properties/SampleName.java | 2 +-
.../pulse/search/direction/ComplexPath.java | 6 +-
.../direction/CompositePathOptimiser.java | 10 +-
.../search/direction/GradientGuidedPath.java | 11 +-
.../pulse/search/direction/PathOptimiser.java | 3 -
.../pulse/search/linear/LinearOptimiser.java | 11 +-
.../pulse/search/linear/WolfeOptimiser.java | 45 ++---
.../search/statistics/CorrelationTest.java | 4 +-
src/main/java/pulse/tasks/Calculation.java | 12 +-
src/main/java/pulse/tasks/SearchTask.java | 66 +++----
src/main/java/pulse/tasks/TaskManager.java | 74 ++++----
src/main/java/pulse/tasks/logs/Status.java | 9 +-
src/main/java/pulse/ui/Launcher.java | 6 +-
.../pulse/ui/components/CalculationTable.java | 2 +
src/main/java/pulse/ui/components/Chart.java | 4 +-
.../java/pulse/ui/components/DataLoader.java | 8 +-
.../java/pulse/ui/components/ResultTable.java | 2 +-
.../ui/components/buttons/LoaderButton.java | 15 +-
.../controllers/InstanceCellEditor.java | 9 +-
.../components/models/ResultTableModel.java | 81 +++++----
.../ui/components/panels/ProblemToolbar.java | 6 +-
src/main/java/pulse/util/PropertyHolder.java | 1 +
src/main/resources/NumericProperty.xml | 8 +-
src/main/resources/Version.txt | 2 +-
src/main/resources/messages.properties | 9 +-
src/test/java/test/NonscatteringSetup.java | 3 +-
106 files changed, 1689 insertions(+), 1587 deletions(-)
delete mode 100644 src/main/java/pulse/problem/schemes/LayeredGrid2D.java
delete mode 100644 src/main/java/pulse/problem/schemes/Partition.java
create mode 100644 src/main/java/pulse/problem/schemes/rte/dom/CornetteSchanksPF.java
delete mode 100644 src/main/java/pulse/problem/schemes/solvers/ADILayeredSolver.java
create mode 100644 src/main/java/pulse/problem/schemes/solvers/ExplicitCoupledSolverNL.java
create mode 100644 src/main/java/pulse/problem/schemes/solvers/ImplicitCoupledSolverNL.java
create mode 100644 src/main/java/pulse/problem/schemes/solvers/MixedCoupledSolverNL.java
delete mode 100644 src/main/java/pulse/problem/statements/CoreShellProblem.java
diff --git a/pom.xml b/pom.xml
index e27af65..3dc51b9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
kotik-coder
PULsE
- 1.94
+ 1.94F
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/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 extends PulseTemporalShape> pulseDescriptor = new InstanceDescriptor(
- "Pulse Shape Selector", PulseTemporalShape.class);
+ private InstanceDescriptor extends PulseTemporalShape> 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 extends AbstractReader> 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..8160344 100644
--- a/src/main/java/pulse/problem/laser/DiscretePulse.java
+++ b/src/main/java/pulse/problem/laser/DiscretePulse.java
@@ -2,6 +2,8 @@
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;
@@ -16,10 +18,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.
+ */
+ public final static int WIDTH_TOLERANCE_FACTOR = 1000;
/**
* This creates a one-dimensional discrete pulse on a {@code grid}.
@@ -34,25 +49,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);
+ normalise();
+ }
/**
* Uses the {@code PulseTemporalShape} of the {@code Pulse} object to
@@ -62,7 +85,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 +94,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 / WIDTH_TOLERANCE_FACTOR;
+
+ 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);
+ } 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.
+ */
+
+ public final void normalise() {
+ invTotalEnergy = 1.0;
+ var pulseShape = pulse.getPulseShape();
+
+ var integrator = new MidpointIntegrator(new Segment(0, widthOnGrid)) {
+
+ @Override
+ public double integrand(double... vars) {
+ return pulseShape.evaluateAt(vars[0]);
+ }
+
+ };
+
+ invTotalEnergy = 1.0 / 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 +173,25 @@ 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 / 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..a34102f 100644
--- a/src/main/java/pulse/problem/laser/DiscretePulse2D.java
+++ b/src/main/java/pulse/problem/laser/DiscretePulse2D.java
@@ -19,7 +19,7 @@
public class DiscretePulse2D extends DiscretePulse {
private double discretePulseSpot;
- private double coordFactor;
+ private double radialFactor;
/**
* The constructor for {@code DiscretePulse2D}.
@@ -35,12 +35,11 @@ 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);
-
+ init(properties);
+
+ properties.addListener(e -> init(properties) );
}
-
+
/**
* This calculates the dimensionless, discretised pulse function at a
* dimensionless radial coordinate {@code coord}.
@@ -59,22 +58,31 @@ public DiscretePulse2D(ClassicalProblem2D problem, Grid2D grid) {
public double evaluateAt(double time, double radialCoord) {
return laserPowerAt(time) * (0.5 + 0.5 * signum(discretePulseSpot - radialCoord));
}
+
+ private void init(ExtendedThermalProperties properties) {
+ radialFactor = (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 radius = (double) pulse.getSpotDiameter().getValue() / 2.0;
+ discretePulseSpot = grid2d.gridRadialDistance(radius, radialFactor);
+ grid2d.adjustStepSize(this);
}
- public double getDiscretePulseSpot() {
+ public final double getDiscretePulseSpot() {
return discretePulseSpot;
}
+
+ public final double getRadialConversionFactor() {
+ return radialFactor;
+ }
}
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 .
- */
- @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 extends Problem> 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 extends Problem>[] 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 extends Problem> 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 extends Problem>[] 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() + ":