- * The dimensional factor is taken from the {@code problem}, while the discrete
- * pulse width (a multiplier of the {@code grid} parameter {@code tau} is
- * calculated using the {@code gridTime} method.
- *
- *
- * @param problem the problem, used to extract the dimensional time factor
- * @param grid a grid used to discretise the {@code pulse}
- */
-
- public DiscretePulse(Problem problem, Grid grid) {
- timeFactor = problem.getProperties().timeFactor();
- this.grid = grid;
- this.pulse = problem.getPulse();
-
- recalculate();
-
- pulse.getPulseShape().init(this);
- pulse.addListener(e -> {
- recalculate();
- pulse.getPulseShape().init(this);
-
- });
- }
-
- /**
- * Uses the {@code PulseTemporalShape} of the {@code Pulse} object to calculate
- * the laser power at the specified moment of {@code time}.
- *
- * @param time the time argument
- * @return the laser power at the specified moment of {@code time}
- */
-
- public double laserPowerAt(double time) {
- return pulse.evaluateAt(time);
- }
-
- /**
- * Recalculates the {@code discretePulseWidth} by calling {@code gridTime} on
- * the physical pulse width and {@code timeFactor}.
- *
- * @see pulse.problem.schemes.Grid.gridTime(double,double)
- */
-
- public void recalculate() {
- final double width = ((Number) pulse.getPulseWidth().getValue()).doubleValue();
- discretePulseWidth = grid.gridTime(width, timeFactor);
- }
-
- /**
- * Gets the discrete pulse width defined by {@code DiscretePulse}.
- *
- * @return a double, representing the discrete pulse width.
- */
-
- public double getDiscreteWidth() {
- return discretePulseWidth;
- }
-
- /**
- * Gets the physical {@code Pulse}
- *
- * @return the {@code Pulse} object
- */
-
- public Pulse getPulse() {
- return pulse;
- }
-
- /**
- * Gets the {@code Grid} object used to construct this {@code DiscretePulse}
- *
- * @return the {@code Grid} object.
- */
-
- public Grid getGrid() {
- return grid;
- }
-
-}
\ No newline at end of file
+public class DiscretePulse implements Serializable {
+
+ private static final long serialVersionUID = 5826506918603729615L;
+ private final Grid grid;
+ private final Pulse pulse;
+ private final ExperimentalData data;
+
+ private double widthOnGrid;
+ private double characteristicTime;
+ 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
+ *
+ * The dimensional factor is taken from the {@code problem}, while the
+ * discrete pulse width (a multiplier of the {@code grid} parameter
+ * {@code tau} is calculated using the {@code gridTime} method.
+ *
+ *
+ * @param problem the problem, used to extract the dimensional time factor
+ * @param grid a grid used to discretise the {@code pulse}
+ */
+ public DiscretePulse(Problem problem, Grid grid) {
+ this.grid = grid;
+ characteristicTime = problem.getProperties().characteristicTime();
+ this.pulse = problem.getPulse();
+
+ Object ancestor
+ = Objects.requireNonNull(problem.specificAncestor(SearchTask.class),
+ "Problem has not been assigned to a SearchTask");
+
+ data = (ExperimentalData) (((SearchTask) ancestor).getInput());
+ init();
+
+ PropertyHolderListener phl = e -> {
+ characteristicTime = problem.getProperties().characteristicTime();
+ widthOnGrid = 0;
+ init();
+ };
+
+ pulse.addListener(e -> {
+ widthOnGrid = 0;
+ init();
+ });
+ problem.addListener(phl);
+
+ }
+
+ /**
+ * Uses the {@code PulseTemporalShape} of the {@code Pulse} object to
+ * calculate the laser power at the specified moment of {@code time}.
+ *
+ * @param time the time argument
+ * @return the laser power at the specified moment of {@code time}
+ */
+ public double laserPowerAt(double time) {
+ return invTotalEnergy * pulse.getPulseShape().evaluateAt(time);
+ }
+
+ /**
+ * Recalculates the {@code discretePulseWidth} by calling {@code gridTime}
+ * on the physical pulse width and {@code timeFactor}.
+ *
+ * @see pulse.problem.schemes.Grid.gridTime(double,double)
+ */
+ public final void init() {
+ final double nominalWidth = ((Number) pulse.getPulseWidth().getValue()).doubleValue();
+ final double resolvedWidth = resolvedPulseWidthSeconds();
+
+ final double EPS = 1E-10;
+
+ double oldValue = widthOnGrid;
+ this.widthOnGrid = pulseWidthGrid();
+
+ /**
+ * The pulse is too short, which makes calculations too expensive. Can
+ * we replace it with a rectangular pulse shape instead?
+ */
+ if (nominalWidth < resolvedWidth - EPS && oldValue < EPS) {
+ //change shape to rectangular
+ var shape = new RectangularPulse();
+ pulse.setPulseShape(shape);
+ shape.init(null, this);
+ } else {
+ pulse.getPulseShape().init(data, this);
+ }
+
+ invTotalEnergy = 1.0 / totalEnergy();
+ }
+
+ /**
+ * Optimises the {@code Grid} parameters so that the timestep is
+ * sufficiently small to enable accurate pulse correction.
+ *
+ * This can change the {@code tauFactor} and {@code tau} variables in the
+ * {@code Grid} object if {@code discretePulseWidth/(M - 1) < grid.tau},
+ * where M is the required number of pulse calculations.
+ *
+ *
+ * @see PulseTemporalShape.getRequiredDiscretisation()
+ */
+ public double pulseWidthGrid() {
+ //minimum number of points for pulse calculation
+ int reqPoints = pulse.getPulseShape().getRequiredDiscretisation();
+ //physical pulse width in time units
+ double experimentalWidth = (double) pulse.getPulseWidth().getValue();
+
+ //minimum resolved pulse width in time units for that specific problem
+ double resolvedWidth = resolvedPulseWidthSeconds();
+
+ double pWidth = Math.max(experimentalWidth, resolvedWidth);
+
+ final double EPS = 1E-10;
+
+ double newTau = pWidth / characteristicTime / reqPoints;
+
+ double result = 0;
+
+ if (newTau < grid.getTimeStep() - EPS) {
+ double newTauFactor = (double) grid.getTimeFactor().getValue() / 2.0;
+ grid.setTimeFactor(derive(TAU_FACTOR, newTauFactor));
+ result = pulseWidthGrid();
+ } else {
+ result = grid.gridTime(pWidth, characteristicTime);
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the total pulse energy using a numerical integrator.The
+ * normalisation factor is then equal to the inverse total energy.
+ *
+ * @return the total pulse energy, assuming sample area fully covered by the
+ * beam
+ */
+ public final double totalEnergy() {
+ var pulseShape = pulse.getPulseShape();
+
+ 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 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
- * Calls the constructor of the superclass, after which calculates the
- * {@code discretePulseSpot} using the {@code gridRadialDistance} method of this
- * class. The dimension factor is defined as the sample diameter.
- *
- *
- * @param problem a two-dimensional problem
- * @param grid the two-dimensional grid
- */
+ /**
+ * The constructor for {@code DiscretePulse2D}.
+ *
+ * Calls the constructor of the superclass, after which calculates the
+ * {@code discretePulseSpot} using the {@code gridRadialDistance} method of
+ * this class. The dimension factor is defined as the sample diameter.
+ *
+ *
+ * @param problem a two-dimensional problem
+ * @param grid the two-dimensional grid
+ */
+ public DiscretePulse2D(ClassicalProblem2D problem, Grid2D grid) {
+ super(problem, grid);
+ var properties = (ExtendedThermalProperties) problem.getProperties();
+ calcPulseSpot(properties);
+ properties.addListener(e -> calcPulseSpot(properties));
+ }
- 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);
+ /**
+ * This calculates the dimensionless, discretised pulse function at a
+ * dimensionless radial coordinate {@code coord}.
+ *
+ * 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)}.
+ *
+ * @param time the time for calculation
+ * @param radialCoord - the radial coordinate [length dimension]
+ * @return the pulse function at {@code time} and {@code coord}, or 0 if
+ * {@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));
+ }
- }
+ /**
+ * 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);
+ }
- /**
- * This calculates the dimensionless, discretised pulse function at a
- * dimensionless radial coordinate {@code coord}.
- *
- * 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)}.
- *
- * @param time the time for calculation
- * @param radialCoord - the radial coordinate [length dimension]
- * @return the pulse function at {@code time} and {@code coord}, or 0 if
- * {@code coord > spotDiameter}.
- * @see pulse.problem.laser.PulseTemporalShape.laserPowerAt(double)
- */
+ private void calcPulseSpot(ExtendedThermalProperties properties) {
+ sampleRadius = (double) properties.getSampleDiameter().getValue() / 2.0;
+ evalPulseSpot();
+ }
- public double evaluateAt(double time, double radialCoord) {
- return laserPowerAt(time) * (0.5 + 0.5 * signum(discretePulseSpot - radialCoord));
- }
+ /**
+ * Calculates the {@code discretePulseSpot} using the
+ * {@code gridRadialDistance} method.
+ *
+ * @see pulse.problem.schemes.Grid2D.gridRadialDistance(double,double)
+ */
+ public final void evalPulseSpot() {
+ var pulse = (Pulse2D) getPhysicalPulse();
+ 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;
+ }
- /**
- * Calls the superclass method, then calculates the {@code discretePulseSpot}
- * using the {@code gridRadialDistance} method.
- *
- * @see pulse.problem.schemes.Grid2D.gridRadialDistance(double,double)
- */
+ public final double getDiscretePulseSpot() {
+ return discretePulseSpot;
+ }
- @Override
- public void recalculate() {
- super.recalculate();
- final double radius = (double) ((Pulse2D) getPulse()).getSpotDiameter().getValue() / 2.0;
- discretePulseSpot = ((Grid2D) getGrid()).gridRadialDistance(radius, coordFactor);
- }
+ public final double getRadialConversionFactor() {
+ return sampleRadius;
+ }
- public double getDiscretePulseSpot() {
- return discretePulseSpot;
- }
+ /**
+ * 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 77f803b0..d4490d2b 100644
--- a/src/main/java/pulse/problem/laser/ExponentiallyModifiedGaussian.java
+++ b/src/main/java/pulse/problem/laser/ExponentiallyModifiedGaussian.java
@@ -6,185 +6,158 @@
import static pulse.properties.NumericProperties.def;
import static pulse.properties.NumericProperties.derive;
import static pulse.properties.NumericProperty.requireType;
-import static pulse.properties.NumericPropertyKeyword.INTEGRATION_SEGMENTS;
import static pulse.properties.NumericPropertyKeyword.SKEW_LAMBDA;
import static pulse.properties.NumericPropertyKeyword.SKEW_MU;
import static pulse.properties.NumericPropertyKeyword.SKEW_SIGMA;
-import java.util.List;
+import java.util.Set;
-import pulse.math.FixedIntervalIntegrator;
-import pulse.math.MidpointIntegrator;
-import pulse.math.Segment;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
-import pulse.properties.Property;
/**
* Represents the exponentially modified Gaussian function, which is given by
* three independent parameters (μ, σ and λ).
- *
+ *
* @see Wikipedia page
*
*/
-
public class ExponentiallyModifiedGaussian extends PulseTemporalShape {
- private double mu;
- private double sigma;
- private double lambda;
- private double norm;
-
- private final static int DEFAULT_POINTS = 100;
- private FixedIntervalIntegrator integrator;
-
- /**
- * Creates an exponentially modified Gaussian with the default parameter values.
- */
-
- public ExponentiallyModifiedGaussian() {
- mu = (double) def(SKEW_MU).getValue();
- lambda = (double) def(SKEW_LAMBDA).getValue();
- sigma = (double) def(SKEW_SIGMA).getValue();
- norm = 1.0;
- integrator = new MidpointIntegrator(new Segment(0.0, getPulseWidth()),
- derive(INTEGRATION_SEGMENTS, DEFAULT_POINTS)) {
-
- @Override
- public double integrand(double... vars) {
- return evaluateAt(vars[0]);
- }
-
- };
- }
-
- /**
- * This calls the superclass {@code init method} and sets the normalisation
- * factor to .
- */
-
- @Override
- public void init(DiscretePulse pulse) {
- super.init(pulse);
- norm = 1.0; // resets the normalisation factor to unity
- norm = 1.0 / area(); // calculates the area. The normalisation factor is then set to the inverse of
- // the area.
- }
-
- /**
- * Uses numeric integration (midpoint rule) to calculate the area of the pulse
- * shape corresponding to the selected parameters.
- *
- * @return the area
- */
-
- private double area() {
- integrator.setBounds(new Segment(0.0, getPulseWidth()));
- return integrator.integrate();
- }
-
- /**
- * Evaluates the laser power function. The error function is calculated using
- * the ApacheCommonsMath library tools.
- *
- * @see https://tinyurl.com/ExpModifiedGaussian
- * @param time is measured from the 'start' of laser pulse
- */
-
- @Override
- public double evaluateAt(double time) {
- final var reducedTime = time / getPulseWidth();
-
- final double lambdaHalf = 0.5 * lambda;
- final double sigmaSq = sigma * sigma;
-
- return norm * lambdaHalf * exp(lambdaHalf * (2.0 * mu + lambda * sigmaSq - 2.0 * reducedTime))
- * erfc((mu + lambda * sigmaSq - reducedTime) / (sqrt(2) * sigma));
-
- }
-
- @Override
- public List listedTypes() {
- var list = super.listedTypes();
- list.add(def(SKEW_MU));
- list.add(def(SKEW_LAMBDA));
- list.add(def(SKEW_SIGMA));
- return list;
- }
-
- /**
- * @return the μ parameter
- */
-
- public NumericProperty getMu() {
- return derive(SKEW_MU, mu);
- }
-
- /**
- * @return the σ parameter
- */
-
- public NumericProperty getSigma() {
- return derive(SKEW_SIGMA, sigma);
- }
-
- /**
- * @return the λ parameter
- */
-
- public NumericProperty getLambda() {
- return derive(SKEW_LAMBDA, lambda);
- }
-
- /**
- * Sets the {@code SKEW_LAMBDA} parameter
- *
- * @param p the λ parameter
- */
-
- public void setLambda(NumericProperty p) {
- requireType(p, SKEW_LAMBDA);
- this.lambda = (double) p.getValue();
- }
-
- /**
- * Sets the {@code SKEW_MU} parameter
- *
- * @param p the μ parameter
- */
-
- public void setMu(NumericProperty p) {
- requireType(p, SKEW_MU);
- this.mu = (double) p.getValue();
- }
-
- /**
- * Sets the {@code SKEW_SIGMA} parameter
- *
- * @param p the σ parameter
- */
-
- public void setSigma(NumericProperty p) {
- requireType(p, SKEW_SIGMA);
- this.sigma = (double) p.getValue();
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- switch (type) {
- case SKEW_MU:
- setMu(property);
- break;
- case SKEW_LAMBDA:
- setLambda(property);
- break;
- case SKEW_SIGMA:
- setSigma(property);
- break;
- default:
- break;
- }
- firePropertyChanged(this, property);
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = -4437794069527301235L;
+ private double mu;
+ private double sigma;
+ private double lambda;
+
+ private final static int MIN_POINTS = 10;
+
+ /**
+ * Creates an exponentially modified Gaussian with the default parameter
+ * values.
+ */
+ public ExponentiallyModifiedGaussian() {
+ mu = (double) def(SKEW_MU).getValue();
+ lambda = (double) def(SKEW_LAMBDA).getValue();
+ sigma = (double) def(SKEW_SIGMA).getValue();
+ }
+
+ public ExponentiallyModifiedGaussian(ExponentiallyModifiedGaussian another) {
+ super(another);
+ this.mu = another.mu;
+ this.sigma = another.sigma;
+ this.lambda = another.lambda;
+ }
+
+ /**
+ * Evaluates the laser power function. The error function is calculated
+ * using the ApacheCommonsMath library tools.
+ *
+ * @see https://tinyurl.com/ExpModifiedGaussian
+ * @param time is measured from the 'start' of laser pulse
+ */
+ @Override
+ public double evaluateAt(double time) {
+ final var reducedTime = time / getPulseWidth();
+
+ final double lambdaHalf = 0.5 * lambda;
+ final double sigmaSq = sigma * sigma;
+
+ return lambdaHalf * exp(lambdaHalf * (2.0 * mu + lambda * sigmaSq - 2.0 * reducedTime))
+ * erfc((mu + lambda * sigmaSq - reducedTime) / (sqrt(2) * sigma));
+
+ }
+
+ /**
+ * @see pulse.properties.NumericPropertyKeyword.SKEW_MU
+ * @see pulse.properties.NumericPropertyKeyword.SKEW_LAMBDA
+ * @see pulse.properties.NumericPropertyKeyword.SKEW_SIGMA
+ */
+ @Override
+ public Set listedKeywords() {
+ var set = super.listedKeywords();
+ set.add(SKEW_MU);
+ set.add(SKEW_LAMBDA);
+ set.add(SKEW_SIGMA);
+ return set;
+ }
+
+ /**
+ * @return the μ parameter
+ */
+ public NumericProperty getMu() {
+ return derive(SKEW_MU, mu);
+ }
+
+ /**
+ * @return the σ parameter
+ */
+ public NumericProperty getSigma() {
+ return derive(SKEW_SIGMA, sigma);
+ }
+
+ /**
+ * @return the λ parameter
+ */
+ public NumericProperty getLambda() {
+ return derive(SKEW_LAMBDA, lambda);
+ }
+
+ /**
+ * Sets the {@code SKEW_LAMBDA} parameter
+ *
+ * @param p the λ parameter
+ */
+ public void setLambda(NumericProperty p) {
+ requireType(p, SKEW_LAMBDA);
+ this.lambda = (double) p.getValue();
+ }
+
+ /**
+ * Sets the {@code SKEW_MU} parameter
+ *
+ * @param p the μ parameter
+ */
+ public void setMu(NumericProperty p) {
+ requireType(p, SKEW_MU);
+ this.mu = (double) p.getValue();
+ }
+
+ /**
+ * Sets the {@code SKEW_SIGMA} parameter
+ *
+ * @param p the σ parameter
+ */
+ public void setSigma(NumericProperty p) {
+ requireType(p, SKEW_SIGMA);
+ this.sigma = (double) p.getValue();
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ switch (type) {
+ case SKEW_MU:
+ setMu(property);
+ break;
+ case SKEW_LAMBDA:
+ setLambda(property);
+ break;
+ case SKEW_SIGMA:
+ setSigma(property);
+ break;
+ default:
+ break;
+ }
+ firePropertyChanged(this, property);
+ }
+
+ @Override
+ public PulseTemporalShape copy() {
+ return new ExponentiallyModifiedGaussian(this);
+ }
+
+ @Override
+ public int getRequiredDiscretisation() {
+ return MIN_POINTS;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/laser/NumericPulse.java b/src/main/java/pulse/problem/laser/NumericPulse.java
new file mode 100644
index 00000000..a71d65fd
--- /dev/null
+++ b/src/main/java/pulse/problem/laser/NumericPulse.java
@@ -0,0 +1,176 @@
+package pulse.problem.laser;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import static pulse.properties.NumericProperties.derive;
+import static pulse.properties.NumericPropertyKeyword.PULSE_WIDTH;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.interpolation.AkimaSplineInterpolator;
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+
+import pulse.input.ExperimentalData;
+import pulse.problem.statements.Problem;
+import pulse.properties.NumericProperty;
+import pulse.properties.NumericPropertyKeyword;
+import pulse.tasks.SearchTask;
+
+import pulse.baseline.FlatBaseline;
+import pulse.tasks.Calculation;
+import pulse.util.FunctionSerializer;
+
+/**
+ * A numeric pulse is given by a set of discrete {@code NumericPulseData}
+ * measured independently using a pulse diode.
+ *
+ * @see pulse.problem.laser.NumericPulseData
+ *
+ */
+public class NumericPulse extends PulseTemporalShape {
+
+ private static final long serialVersionUID = 6088261629992349844L;
+ private NumericPulseData pulseData;
+ private transient UnivariateFunction interpolation;
+
+ private final static int MIN_POINTS = 20;
+
+ public NumericPulse() {
+ //intentionally blank
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param pulse another numeric pulse, the data of which will be copied
+ */
+ public NumericPulse(NumericPulse pulse) {
+ super(pulse);
+ this.pulseData = new NumericPulseData(pulseData);
+ }
+
+ /**
+ * Defines the pulse width as the last element of the time sequence
+ * contained in {@code NumericPulseData}. Calls {@code super.init}, then
+ * interpolates the input pulse using spline functions and normalises the
+ * output.
+ *
+ * @param data
+ * @see normalise()
+ *
+ */
+ @Override
+ public void init(ExperimentalData data, DiscretePulse pulse) {
+ //generate baseline-subtracted numeric data from ExperimentalData
+ baselineSubtractedFrom(data);
+
+ //notify host pulse object of a new pulse width
+ var problem = ((Calculation) ((SearchTask) data.getParent())
+ .getResponse()).getProblem();
+ setPulseWidthOf(problem);
+
+ //convert to dimensionless time and interpolate
+ double timeFactor = problem.getProperties().characteristicTime();
+ doInterpolation(timeFactor);
+ }
+
+ /**
+ * Copies the numeric pulse from metadata and subtracts a horizontal
+ * baseline from the data points assigned to {@code pulseData}.
+ *
+ * @param data the experimental data containing the metadata with numeric
+ * pulse data.
+ */
+ private void baselineSubtractedFrom(ExperimentalData data) {
+ pulseData = new NumericPulseData(data.getMetadata().getPulseData());
+
+ //subtracts a horizontal baseline from the pulse data
+ var baseline = new FlatBaseline();
+ baseline.fitTo(pulseData);
+
+ for (int i = 0, size = pulseData.getTimeSequence().size(); i < size; i++) {
+ pulseData.setSignalAt(i,
+ pulseData.signalAt(i) - baseline.valueAt(pulseData.timeAt(i)));
+ }
+ }
+
+ private void setPulseWidthOf(Problem problem) {
+ var timeSequence = pulseData.getTimeSequence();
+ double pulseWidth = timeSequence.get(timeSequence.size() - 1);
+
+ var pulseObject = problem.getPulse();
+ pulseObject.setPulseWidth(derive(PULSE_WIDTH, pulseWidth));
+
+ }
+
+ private void doInterpolation(double timeFactor) {
+ var interpolator = new AkimaSplineInterpolator();
+
+ 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());
+ }
+
+ /**
+ * If the argument is less than the pulse width, uses a spline to
+ * interpolate the pulse data at {@code time}. Otherwise returns zero.
+ */
+ @Override
+ public double evaluateAt(double time) {
+ return time > getPulseWidth() ? 0.0 : interpolation.value(time);
+ }
+
+ @Override
+ public PulseTemporalShape copy() {
+ return new NumericPulse();
+ }
+
+ /**
+ * Does not define any property.
+ */
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ // TODO Auto-generated method stub
+ }
+
+ public NumericPulseData getData() {
+ return pulseData;
+ }
+
+ public void setData(NumericPulseData pulseData) {
+ this.pulseData = pulseData;
+
+ }
+
+ public UnivariateFunction getInterpolation() {
+ return interpolation;
+ }
+
+ @Override
+ public int getRequiredDiscretisation() {
+ return MIN_POINTS;
+ }
+
+ /*
+ Serialization
+ */
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException {
+ // default serialization
+ oos.defaultWriteObject();
+ // write the object
+ FunctionSerializer.writeSplineFunction((PolynomialSplineFunction) interpolation, oos);
+ }
+
+ private void readObject(ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ // default deserialization
+ ois.defaultReadObject();
+ this.interpolation = FunctionSerializer.readSplineFunction(ois);
+ }
+
+}
diff --git a/src/main/java/pulse/problem/laser/NumericPulseData.java b/src/main/java/pulse/problem/laser/NumericPulseData.java
new file mode 100644
index 00000000..7bbfeeed
--- /dev/null
+++ b/src/main/java/pulse/problem/laser/NumericPulseData.java
@@ -0,0 +1,78 @@
+package pulse.problem.laser;
+
+import java.util.List;
+import pulse.AbstractData;
+import pulse.DiscreteInput;
+import pulse.input.IndexRange;
+import pulse.input.Range;
+
+/**
+ * An instance of the {@code AbstractData} class, which also declares an
+ * {@code externalID}. Use to store numeric data of the pulse for each
+ * measurement imported from an external source.
+ *
+ */
+public class NumericPulseData extends AbstractData implements DiscreteInput {
+
+ private static final long serialVersionUID = 8142129124831241206L;
+ private final int externalID;
+
+ /**
+ * Stores {@code id} and calls super-constructor
+ *
+ * @param id an external ID defined in the imported file
+ */
+ public NumericPulseData(int id) {
+ super();
+ this.externalID = id;
+ }
+
+ /**
+ * Copies everything, including the id number.
+ *
+ * @param data another object
+ */
+ public NumericPulseData(NumericPulseData data) {
+ super(data);
+ this.externalID = data.externalID;
+ }
+
+ /**
+ * Adds a data point to the internal storage and increments counter.
+ */
+ @Override
+ public void addPoint(double time, double power) {
+ super.addPoint(time, power);
+ super.incrementCount();
+ }
+
+ /**
+ * Gets the external ID usually specified in the experimental files. Note
+ * this is not a {@code NumericProperty}
+ *
+ * @return an integer, representing the external ID
+ */
+ public int getExternalID() {
+ return externalID;
+ }
+
+ public double pulseWidth() {
+ return super.timeLimit();
+ }
+
+ @Override
+ public List getX() {
+ return getTimeSequence();
+ }
+
+ @Override
+ public List getY() {
+ return getSignalData();
+ }
+
+ @Override
+ public IndexRange getIndexRange() {
+ return new IndexRange(this.getTimeSequence(), Range.UNLIMITED);
+ }
+
+}
diff --git a/src/main/java/pulse/problem/laser/PulseTemporalShape.java b/src/main/java/pulse/problem/laser/PulseTemporalShape.java
index ed24ec38..9a74c09c 100644
--- a/src/main/java/pulse/problem/laser/PulseTemporalShape.java
+++ b/src/main/java/pulse/problem/laser/PulseTemporalShape.java
@@ -1,50 +1,69 @@
package pulse.problem.laser;
+import pulse.input.ExperimentalData;
import pulse.util.PropertyHolder;
import pulse.util.Reflexive;
/**
* An abstract time-dependent pulse shape. Declares the abstract method to
* calculate the laser power function at a given moment of time. This generally
- * utilises a discrete pulse width.
+ * utilises a discrete pulse width. By default, uses a midpoint-rule numeric
+ * integrator to calculate the pulse integral.
*
*/
-
public abstract class PulseTemporalShape extends PropertyHolder implements Reflexive {
- private double width;
+ private double width;
+
+ public PulseTemporalShape() {
+ //intentionlly blank
+ }
+
+ public PulseTemporalShape(PulseTemporalShape another) {
+ this.width = another.width;
+ }
+
+ /**
+ * This evaluates the dimensionless, discretised pulse function on a
+ * {@code grid} needed to evaluate the heat source in the difference scheme.
+ *
+ * @param time the dimensionless time (a multiplier of {@code tau}), at
+ * which calculation should be performed
+ * @return a double value, representing the pulse function at {@code time}
+ */
+ public abstract double evaluateAt(double time);
- /**
- * This evaluates the dimensionless, discretised pulse function on a
- * {@code grid} needed to evaluate the heat source in the difference scheme.
- *
- * @param time the dimensionless time (a multiplier of {@code tau}), at which
- * calculation should be performed
- * @return a double value, representing the pulse function at {@code time}
- */
+ /**
+ * 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();
+ }
- public abstract double evaluateAt(double time);
+ public abstract PulseTemporalShape copy();
- public void init(DiscretePulse pulse) {
- width = pulse.getDiscreteWidth();
- }
+ @Override
+ public String getPrefix() {
+ return "Pulse temporal shape";
+ }
- @Override
- public String getPrefix() {
- return "Pulse temporal shape";
- }
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
+ public double getPulseWidth() {
+ return width;
+ }
- public double getPulseWidth() {
- return width;
- }
+ public void setPulseWidth(double width) {
+ this.width = width;
+ }
- 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 fa336720..7bc5dfbd 100644
--- a/src/main/java/pulse/problem/laser/RectangularPulse.java
+++ b/src/main/java/pulse/problem/laser/RectangularPulse.java
@@ -9,25 +9,37 @@
* The simplest pulse shape defined as , where
* is the signum function, pulse is the pulse width.
- *
+ *
* @see java.lang.Math.signum(double)
*/
-
public class RectangularPulse extends PulseTemporalShape {
- /**
- * @param time the time measured from the start of the laser pulse.
- */
-
- @Override
- public double evaluateAt(double time) {
- var width = getPulseWidth();
- return 0.5 / width * (1 + signum(width - time));
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- // intentionally blak
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = 8207478409316696745L;
+ private final static int MIN_POINTS = 2;
+
+ /**
+ * @param time the time measured from the start of the laser pulse.
+ * @return
+ */
+ @Override
+ public double evaluateAt(double time) {
+ var width = getPulseWidth();
+ return 0.5 / width * (1 + signum(width - time));
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ // intentionally blak
+ }
+
+ @Override
+ 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 570af2f9..908e2cfc 100644
--- a/src/main/java/pulse/problem/laser/TrapezoidalPulse.java
+++ b/src/main/java/pulse/problem/laser/TrapezoidalPulse.java
@@ -6,122 +6,130 @@
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.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
-import pulse.properties.Property;
/**
* A trapezoidal pulse shape, which combines a rise segment, a constant-power
* segment, and a fall segment. The rise and fall ratios can be changed.
*
*/
-
public class TrapezoidalPulse extends PulseTemporalShape {
- private double rise;
- private double fall;
- private double h;
-
- /**
- * Constructs a trapezoidal pulse using a default segmentation principle. The
- * reader is referred to the {@code .xml} file containing the default values of
- * {@code TRAPEZOIDAL_RISE_PERCENTAGE} and {@code TRAPEZOIDAL_FALL_PERCENTAGE}.
- * The maximum laser power is adjusted to ensure the area of the shape is equal
- * to unity.
- */
-
- public TrapezoidalPulse() {
- rise = (int) def(TRAPEZOIDAL_RISE_PERCENTAGE).getValue() / 100.0;
- fall = (int) def(TRAPEZOIDAL_FALL_PERCENTAGE).getValue() / 100.0;
- h = height();
- }
-
- @Override
- public void init(DiscretePulse pulse) {
- super.init(pulse);
- h = height();
- }
-
- /**
- * Calculates the height of the trapezium which under current segmentation will
- * yield an area of unity.
- *
- * @return the calculated height of the constant segmment
- */
-
- private double height() {
- return 2.0 / (getPulseWidth() * (2.0 - rise - fall));
- }
-
- /**
- * Calculates power using a piecewise function, which corresponds to either a
- * linearly changing, a constant laser power or zero.
- *
- * @param time the time measured from the start of the laser pulse.
- */
-
- @Override
- public double evaluateAt(double time) {
- final var reducedTime = time / getPulseWidth();
-
- double result = 0;
-
- if (reducedTime < rise) { // triangular
- result = reducedTime * h / rise;
- } else if (reducedTime < 1.0 - fall) { // rectangular
- result = h;
- } else if (reducedTime < 1.0) { // triangular
- final var t2 = (reducedTime - (1.0 - fall));
- result = (fall - t2) * h / fall;
- }
-
- return result;
-
- }
-
- @Override
- public List listedTypes() {
- var list = super.listedTypes();
- list.add(def(TRAPEZOIDAL_RISE_PERCENTAGE));
- list.add(def(TRAPEZOIDAL_FALL_PERCENTAGE));
- return list;
- }
-
- public NumericProperty getTrapezoidalRise() {
- return derive(TRAPEZOIDAL_RISE_PERCENTAGE, (int) (rise * 100));
- }
-
- public NumericProperty getTrapezoidalFall() {
- return derive(TRAPEZOIDAL_FALL_PERCENTAGE, (int) (fall * 100));
- }
-
- public void setTrapezoidalRise(NumericProperty p) {
- requireType(p, TRAPEZOIDAL_RISE_PERCENTAGE);
- this.rise = (int) p.getValue() / 100.0;
- h = height();
- }
-
- public void setTrapezoidalFall(NumericProperty p) {
- requireType(p, TRAPEZOIDAL_FALL_PERCENTAGE);
- this.fall = (int) p.getValue() / 100.0;
- h = height();
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- switch (type) {
- case TRAPEZOIDAL_RISE_PERCENTAGE:
- setTrapezoidalRise(property);
- break;
- case TRAPEZOIDAL_FALL_PERCENTAGE:
- setTrapezoidalFall(property);
- break;
- default:
- break;
- }
- firePropertyChanged(this, property);
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = 2089809680713225034L;
+ private double rise;
+ private double fall;
+ private double h;
+
+ private final static int MIN_POINTS = 8;
+
+ /**
+ * Constructs a trapezoidal pulse using a default segmentation principle.
+ * The reader is referred to the {@code .xml} file containing the default
+ * values of {@code TRAPEZOIDAL_RISE_PERCENTAGE} and
+ * {@code TRAPEZOIDAL_FALL_PERCENTAGE}. The maximum laser power is adjusted
+ * to ensure the area of the shape is equal to unity.
+ */
+ public TrapezoidalPulse() {
+ rise = (int) def(TRAPEZOIDAL_RISE_PERCENTAGE).getValue() / 100.0;
+ fall = (int) def(TRAPEZOIDAL_FALL_PERCENTAGE).getValue() / 100.0;
+ h = 1.0;
+ }
+
+ public TrapezoidalPulse(TrapezoidalPulse another) {
+ this.rise = another.rise;
+ this.fall = another.fall;
+ this.h = another.h;
+ }
+
+ /**
+ * Calculates the height of the trapezium which under current segmentation
+ * will yield an area of unity.
+ *
+ * @return the calculated height of the constant segmment
+ */
+ private double height() {
+ return 2.0 / (getPulseWidth() * (2.0 - rise - fall));
+ }
+
+ /**
+ * Calculates power using a piecewise function, which corresponds to either
+ * a linearly changing, a constant laser power or zero.
+ *
+ * @param time the time measured from the start of the laser pulse.
+ */
+ @Override
+ public double evaluateAt(double time) {
+ final var reducedTime = time / getPulseWidth();
+
+ double result = 0;
+
+ if (reducedTime < rise) { // triangular
+ result = reducedTime * h / rise;
+ } else if (reducedTime < 1.0 - fall) { // rectangular
+ result = h;
+ } else if (reducedTime < 1.0) { // triangular
+ final var t2 = (reducedTime - (1.0 - fall));
+ result = (fall - t2) * h / fall;
+ }
+
+ return result;
+
+ }
+
+ @Override
+ public Set listedKeywords() {
+ var set = super.listedKeywords();
+ set.add(TRAPEZOIDAL_RISE_PERCENTAGE);
+ set.add(TRAPEZOIDAL_FALL_PERCENTAGE);
+ return set;
+ }
+
+ public NumericProperty getTrapezoidalRise() {
+ return derive(TRAPEZOIDAL_RISE_PERCENTAGE, (int) (rise * 100));
+ }
+
+ public NumericProperty getTrapezoidalFall() {
+ return derive(TRAPEZOIDAL_FALL_PERCENTAGE, (int) (fall * 100));
+ }
+
+ public void setTrapezoidalRise(NumericProperty p) {
+ requireType(p, TRAPEZOIDAL_RISE_PERCENTAGE);
+ this.rise = (int) p.getValue() / 100.0;
+ h = height();
+ }
+
+ public void setTrapezoidalFall(NumericProperty p) {
+ requireType(p, TRAPEZOIDAL_FALL_PERCENTAGE);
+ this.fall = (int) p.getValue() / 100.0;
+ h = height();
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ switch (type) {
+ case TRAPEZOIDAL_RISE_PERCENTAGE:
+ setTrapezoidalRise(property);
+ break;
+ case TRAPEZOIDAL_FALL_PERCENTAGE:
+ setTrapezoidalFall(property);
+ break;
+ default:
+ break;
+ }
+ firePropertyChanged(this, property);
+ }
+
+ @Override
+ public PulseTemporalShape copy() {
+ return new TrapezoidalPulse(this);
+ }
+
+ @Override
+ public int getRequiredDiscretisation() {
+ return MIN_POINTS;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/laser/package-info.java b/src/main/java/pulse/problem/laser/package-info.java
index 21573e60..3e4cf687 100644
--- a/src/main/java/pulse/problem/laser/package-info.java
+++ b/src/main/java/pulse/problem/laser/package-info.java
@@ -1,5 +1,4 @@
/**
* This package deals with discrete laser pulse representation and their various temporal shapes.
*/
-
-package pulse.problem.laser;
\ No newline at end of file
+package pulse.problem.laser;
diff --git a/src/main/java/pulse/problem/schemes/ADIScheme.java b/src/main/java/pulse/problem/schemes/ADIScheme.java
index 75e989c1..b20b34db 100644
--- a/src/main/java/pulse/problem/schemes/ADIScheme.java
+++ b/src/main/java/pulse/problem/schemes/ADIScheme.java
@@ -12,54 +12,66 @@
* needed to solve a {@code Problem}.
*
*/
-
public abstract class ADIScheme extends DifferenceScheme {
- /**
- * Creates a new {@code ADIScheme} with default values of grid density and time
- * factor.
- */
-
- public ADIScheme() {
- this(derive(GRID_DENSITY, 30), derive(TAU_FACTOR, 1.0));
- }
-
- /**
- * Creates an {@code ADIScheme} with the specified arguments. This creates an
- * associated {@code Grid2D} object.
- *
- * @param N the grid density
- * @param timeFactor the time factor (τF)
- */
+ /**
+ *
+ */
+ private static final long serialVersionUID = 4772650159522354367L;
- public ADIScheme(NumericProperty N, NumericProperty timeFactor) {
- super();
- setGrid(new Grid2D(N, timeFactor));
- }
+ /**
+ * Creates a new {@code ADIScheme} with default values of grid density and
+ * time factor.
+ */
+ public ADIScheme() {
+ this(derive(GRID_DENSITY, 30), derive(TAU_FACTOR, 0.5));
+ }
- /**
- * Creates an {@code ADIScheme} with the specified arguments. This creates an
- * associated {@code Grid2D} object.
- *
- * @param N the grid density
- * @param timeFactor the time factor (τF)
- * @param timeLimit a custom time limit (tlim)
- */
+ /**
+ * Creates an {@code ADIScheme} with the specified arguments. This creates
+ * an associated {@code Grid2D} object.
+ *
+ * @param N the grid density
+ * @param timeFactor the time factor (τF)
+ */
+ public ADIScheme(NumericProperty N, NumericProperty timeFactor) {
+ super();
+ setGrid(new Grid2D(N, timeFactor));
+ }
- public ADIScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
- setTimeLimit(timeLimit);
- setGrid(new Grid2D(N, timeFactor));
- }
+ /**
+ * Creates an {@code ADIScheme} with the specified arguments. This creates
+ * an associated {@code Grid2D} object.
+ *
+ * @param N the grid density
+ * @param timeFactor the time factor (τF)
+ * @param timeLimit a custom time limit (tlim)
+ */
+ public ADIScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
+ setTimeLimit(timeLimit);
+ setGrid(new Grid2D(N, timeFactor));
+ }
- /**
- * Prints out the description of this problem type.
- *
- * @return a verbose description of the problem.
- */
+ /**
+ * Prints out the description of this problem type.
+ *
+ * @return a verbose description of the problem.
+ */
+ @Override
+ public String toString() {
+ return getString("ADIScheme.4");
+ }
- @Override
- 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
+ }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java b/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java
index a1530d34..450db9db 100644
--- a/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java
+++ b/src/main/java/pulse/problem/schemes/BlockMatrixAlgorithm.java
@@ -1,79 +1,85 @@
package pulse.problem.schemes;
/**
- * A modification of the algorithm for solving a system of linear equations, where the first and last equation contains references
- * to the last and first elements of the solution, respectively. The corresponding matrix is composed of an inner tridiagonal block
- * and a border formed by an extra row and column. This block system is solved using the Sherman-Morrison-Woodbury identity and the
- * Thomas algorithm for the main block.
+ * A modification of the algorithm for solving a system of linear equations,
+ * where the first and last equation contains references to the last and first
+ * elements of the solution, respectively. The corresponding matrix is composed
+ * of an inner tridiagonal block and a border formed by an extra row and column.
+ * This block system is solved using the Sherman-Morrison-Woodbury identity and
+ * the Thomas algorithm for the main block.
*
*/
-
public class BlockMatrixAlgorithm extends TridiagonalMatrixAlgorithm {
- private double[] gamma;
- private double[] p;
- private 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];
- }
-
- @Override
- public void sweep(double[] V) {
- final int N = V.length - 1;
- for (int j = N - 1; j >= 0; j--)
- V[j] = p[j] + V[N] * q[j];
- }
-
- @Override
- public void evaluateBeta(final double[] U) {
- super.evaluateBeta(U);
- final int N = getGrid().getGridDensityValue();
- var alpha = getAlpha();
- var beta = getBeta();
-
- p[N - 1] = beta[N];
- q[N - 1] = alpha[N] + gamma[N];
-
- for (int i = N - 2; i >= 0; i--) {
- p[i] = alpha[i + 1] * p[i + 1] + beta[i + 1];
- q[i] = alpha[i + 1] * q[i + 1] + gamma[i + 1];
- }
- }
-
- @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 a = getCoefA();
- final double b = getCoefB();
-
- for (int i = start; i < endExclusive; i++) {
- setBeta(i, beta(U[i - 1] * HX2_TAU, phi(i - 1), i));
- setGamma(i, a * gamma[i - 1] / (b - a * alpha[i - 1]));
- }
-
- }
-
- public double[] getP() {
- return p;
- }
-
- public double[] getQ() {
- return q;
- }
-
- public void setGamma(final int i, final double g) {
- this.gamma[i] = g;
- }
-
- public double[] getGamma() {
- return gamma;
- }
+ private static final long serialVersionUID = -6553638438386098008L;
+ private final double[] gamma;
+ private final double[] p;
+ private final double[] q;
+
+ public BlockMatrixAlgorithm(Grid grid) {
+ super(grid);
+ final int N = this.getGridPoints();
+ gamma = new double[N + 2];
+ p = new double[N];
+ q = new double[N];
+ }
+
+ @Override
+ public void sweep(double[] V) {
+ final int N = V.length - 1;
+ for (int j = N - 1; j >= 0; j--) {
+ V[j] = p[j] + V[N] * q[j];
+ }
+ }
+
+ @Override
+ public void evaluateBeta(final double[] U) {
+ super.evaluateBeta(U);
+ var alpha = getAlpha();
+ var beta = getBeta();
+
+ final int N = getGridPoints();
+
+ p[N - 1] = beta[N];
+ q[N - 1] = alpha[N] + gamma[N];
+
+ for (int i = N - 2; i >= 0; i--) {
+ p[i] = alpha[i + 1] * p[i + 1] + beta[i + 1];
+ q[i] = alpha[i + 1] * q[i + 1] + gamma[i + 1];
+ }
+ }
+
+ @Override
+ public void evaluateBeta(final double[] U, final int start, final int endExclusive) {
+ var alpha = getAlpha();
+
+ final double h = this.getGridStep();
+ final double HX2_TAU = h * h / this.getTimeStep();
+
+ final double a = getCoefA();
+ final double b = getCoefB();
+
+ for (int i = start; i < endExclusive; i++) {
+ setBeta(i, beta(U[i - 1] * HX2_TAU, phi(i - 1), i));
+ setGamma(i, a * gamma[i - 1] / (b - a * alpha[i - 1]));
+ }
+
+ }
+
+ public double[] getP() {
+ return p;
+ }
+
+ public double[] getQ() {
+ return q;
+ }
+
+ public void setGamma(final int i, final double g) {
+ this.gamma[i] = g;
+ }
+
+ public double[] getGamma() {
+ return gamma;
+ }
}
diff --git a/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java b/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java
index 06ff68bd..17976a27 100644
--- a/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java
+++ b/src/main/java/pulse/problem/schemes/CoupledImplicitScheme.java
@@ -1,113 +1,90 @@
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.List;
+import java.util.Set;
import pulse.problem.schemes.rte.RTECalculationStatus;
+import pulse.problem.schemes.solvers.SolverException;
+import static pulse.problem.schemes.solvers.SolverException.SolverExceptionType.RTE_SOLVER_ERROR;
import pulse.problem.statements.ParticipatingMedium;
import pulse.problem.statements.Problem;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
-import pulse.properties.Property;
-
-public abstract class CoupledImplicitScheme extends ImplicitScheme implements FixedPointIterations {
-
- private RadiativeTransferCoupling coupling;
- private RTECalculationStatus calculationStatus;
- private double nonlinearPrecision;
-
- private double pls;
-
- public CoupledImplicitScheme(NumericProperty N, NumericProperty timeFactor) {
- super();
- setGrid(new Grid(N, timeFactor));
- nonlinearPrecision = (double) def(NONLINEAR_PRECISION).getValue();
- setCoupling(new RadiativeTransferCoupling());
- calculationStatus = RTECalculationStatus.NORMAL;
- }
-
- public CoupledImplicitScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
- this(N, timeFactor);
- setTimeLimit(timeLimit);
- }
-
- @Override
- public void timeStep(final int m) {
- pls = pulse(m);
- doIterations(getCurrentSolution(), nonlinearPrecision, m);
- }
-
- @Override
- public void iteration(final int m) {
- super.timeStep(m);
- }
-
- @Override
- public void finaliseIteration(double[] V) {
- setCalculationStatus( coupling.getRadiativeTransferEquation().compute(V) );
- }
-
- public RadiativeTransferCoupling getCoupling() {
- return coupling;
- }
-
- public void setCoupling(RadiativeTransferCoupling coupling) {
- this.coupling = coupling;
- this.coupling.setParent(this);
- }
-
- @Override
- public void finaliseStep() {
- super.finaliseStep();
- coupling.getRadiativeTransferEquation().getFluxes().store();
- }
-
- @Override
- public List listedTypes() {
- List list = super.listedTypes();
- list.add(def(NONLINEAR_PRECISION));
- return list;
- }
-
- 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() {
- return calculationStatus;
- }
-
- public void setCalculationStatus(RTECalculationStatus calculationStatus) {
- this.calculationStatus = calculationStatus;
- }
-
- public double getCurrentPulseValue() {
- return pls;
- }
-
-}
\ No newline at end of file
+
+public abstract class CoupledImplicitScheme extends ImplicitScheme {
+
+ private static final long serialVersionUID = 1974655675470727643L;
+ private RadiativeTransferCoupling coupling;
+ private RTECalculationStatus calculationStatus;
+ private boolean autoUpdateFluxes = true; //should be false for nonlinear solvers
+
+ public CoupledImplicitScheme(NumericProperty N, NumericProperty timeFactor) {
+ super();
+ setGrid(new Grid(N, timeFactor));
+ setCoupling(new RadiativeTransferCoupling());
+ calculationStatus = RTECalculationStatus.NORMAL;
+ }
+
+ public CoupledImplicitScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
+ this(N, timeFactor);
+ setTimeLimit(timeLimit);
+ }
+
+ @Override
+ public void finaliseStep() throws SolverException {
+ super.finaliseStep();
+ if (autoUpdateFluxes) {
+ var rte = this.getCoupling().getRadiativeTransferEquation();
+ setCalculationStatus(rte.compute(getCurrentSolution()));
+ }
+ coupling.getRadiativeTransferEquation().getFluxes().store();
+ }
+
+ @Override
+ public Set listedKeywords() {
+ var set = super.listedKeywords();
+ set.add(NONLINEAR_PRECISION);
+ return set;
+ }
+
+ @Override
+ public boolean normalOperation() {
+ return super.normalOperation() && (getCalculationStatus() == RTECalculationStatus.NORMAL);
+ }
+
+ public final RTECalculationStatus getCalculationStatus() {
+ return calculationStatus;
+ }
+
+ public final void setCalculationStatus(RTECalculationStatus calculationStatus) throws SolverException {
+ this.calculationStatus = calculationStatus;
+ if (calculationStatus != RTECalculationStatus.NORMAL) {
+ throw new SolverException(calculationStatus.toString(),
+ RTE_SOLVER_ERROR);
+ }
+ }
+
+ public final RadiativeTransferCoupling getCoupling() {
+ return coupling;
+ }
+
+ 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};
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/DifferenceScheme.java b/src/main/java/pulse/problem/schemes/DifferenceScheme.java
index 5bb773df..64de3f8c 100644
--- a/src/main/java/pulse/problem/schemes/DifferenceScheme.java
+++ b/src/main/java/pulse/problem/schemes/DifferenceScheme.java
@@ -1,18 +1,18 @@
package pulse.problem.schemes;
+import java.util.Objects;
import static pulse.properties.NumericProperties.def;
import static pulse.properties.NumericProperties.derive;
import static pulse.properties.NumericProperty.requireType;
import static pulse.properties.NumericPropertyKeyword.TIME_LIMIT;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Set;
import pulse.problem.laser.DiscretePulse;
+import pulse.problem.schemes.solvers.SolverException;
import pulse.problem.statements.Problem;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
-import pulse.properties.Property;
import pulse.util.PropertyHolder;
import pulse.util.Reflexive;
@@ -23,288 +23,304 @@
* partitioning, adjusted to ensure a stable or conditionally-stable behaviour
* of the solution. The {@code Grid} is also used to define a
* {@code DiscretePulse} function.
- *
+ *
* @see pulse.problem.schemes.Grid
* @see pulse.problem.laser.DiscretePulse
*/
-
public abstract class DifferenceScheme extends PropertyHolder implements Reflexive {
- private DiscretePulse discretePulse;
- private Grid grid;
-
- private double timeLimit;
- private int timeInterval;
-
- private static boolean hideDetailedAdjustment = true;
-
- private final static double EPS = 1e-7; // a small value ensuring numeric stability
-
- /**
- * A constructor which merely sets the time limit to its default value.
- */
-
- protected DifferenceScheme() {
- setTimeLimit(def(TIME_LIMIT));
- }
-
- /**
- * A constructor for setting the time limit to a pre-set value.
- *
- * @param timeLimit the calculation time limit
- */
-
- protected DifferenceScheme(NumericProperty timeLimit) {
- setTimeLimit(timeLimit);
- }
-
- /**
- * 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}.
- *
- * @param df the DifferenceScheme to copy from
- */
-
- public void copyFrom(DifferenceScheme df) {
- this.grid = df.getGrid().copy();
- discretePulse = null;
- timeLimit = df.timeLimit;
- }
-
- /**
- *
- * Contains preparatory steps to ensure smooth running of the solver. This
- * includes creating a {@code DiscretePulse} object and calculating the
- * {@code timeInterval}. The latter determines the real-time calculation of a
- * {@code HeatingCurve} based on the numerical solution of {@code problem}; it
- * thus takes into account the difference between the scheme timestep and the
- * {@code HeatingCurve} point spacing. All subclasses of
- * {@code DifferenceScheme} should override and explicitly call this superclass
- * method where appropriate.
- *
- *
- * @param problem the heat problem to be solved
- */
-
- protected void prepare(Problem problem) {
- discretePulse = problem.discretePulseOn(grid);
- grid.adjustTo(discretePulse);
-
- var hc = problem.getHeatingCurve();
- hc.clear();
- }
-
- public void runTimeSequence(Problem problem) {
- runTimeSequence(problem, 0, timeLimit);
- var curve = problem.getHeatingCurve();
- final double maxTemp = (double) problem.getProperties().getMaximumTemperature().getValue();
- curve.scale(maxTemp / curve.apparentMaximum() );
- }
-
- public void runTimeSequence(Problem problem, final double offset, final double endTime) {
- final var grid = getGrid();
-
- var curve = problem.getHeatingCurve();
-
- int adjustedNumPoints = (int)curve.getNumPoints().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 = 1.0; dt < tau; adjustedNumPoints *= factor) {
- dt = timeSegment / (adjustedNumPoints - 1);
- factor = dt / tau;
- timeInterval = (int) factor;
- }
-
- final double wFactor = timeInterval * tau * problem.getProperties().timeFactor();
-
- // First point (index = 0) is always (0.0, 0.0)
-
- /*
- * 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) {
-
- /*
- * 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);
- curve.addPoint(nextTime, signal());
-
- }
-
- }
-
- private void timeSegment(final int m1, final int m2) {
- for (int m = m1; m < m2 && normalOperation(); m++) {
- timeStep(m);
- finaliseStep();
- }
- }
-
- public double pulse(final int m) {
- return getDiscretePulse().laserPowerAt((m - EPS) * getGrid().getTimeStep());
- }
-
- public abstract double signal();
-
- public abstract void timeStep(final int m);
-
- public abstract void finaliseStep();
-
- public boolean normalOperation() {
- return true;
- }
-
- /**
- * The superclass only lists the {@code TIME_LIMIT} property.
- */
-
- @Override
- public List listedTypes() {
- List list = new ArrayList<>();
- list.add(def(TIME_LIMIT));
- return list;
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName();
- }
-
- /**
- * Gets the discrete representation of {@code Pulse} on the {@code Grid}.
- *
- * @return the discrete pulse
- * @see pulse.problem.statements.Pulse
- */
-
- public DiscretePulse getDiscretePulse() {
- return discretePulse;
- }
-
- /**
- * Gets the {@code Grid} object defining partioning used in this
- * {@code DifferenceScheme}
- *
- * @return the grid
- */
-
- public Grid getGrid() {
- return grid;
- }
-
- /**
- * Sets the grid and adopts it as its child.
- *
- * @param grid the grid
- */
-
- public void setGrid(Grid grid) {
- this.grid = grid;
- this.grid.setParent(this);
- }
-
- /**
- * The time interval is the number of discrete timesteps that will be discarded
- * when storing the resulting solution into a {@code HeatingCurve} object, thus
- * ensuring that only a limited set of points is stored.
- *
- * @return the time interval
- */
-
- public int getTimeInterval() {
- return timeInterval;
- }
-
- /**
- * Sets the time interval to the argument of this method.
- *
- * @param timeInterval a positive integer.
- */
-
- public void setTimeInterval(int timeInterval) {
- this.timeInterval = timeInterval;
- }
-
- /**
- * If true, Lets the UI know that the user only wants to have the most important
- * properties displayed. Otherwise this will signal all properties need to be
- * displayed.
- */
-
- @Override
- public boolean areDetailsHidden() {
- return hideDetailedAdjustment;
- }
-
- /**
- * Changes the policy of displaying a detailed information about this scheme.
- *
- * @param b a boolean.
- */
-
- public static void setDetailsHidden(boolean b) {
- hideDetailedAdjustment = b;
- }
-
- /**
- * The time limit (in whatever units this {@code DifferenceScheme} uses to
- * process the solution), which serves as the ultimate breakpoint for the
- * calculations.
- *
- * @return the {@code NumericProperty} with the type {@code TIME_LIMIT}
- * @see pulse.properties.NumericPropertyKeyword
- */
-
- public NumericProperty getTimeLimit() {
- return derive(TIME_LIMIT, timeLimit);
- }
-
- /**
- * Sets the time limit (in units defined by the corresponding
- * {@code NumericProperty}), which serves as the breakpoint for the
- * calculations.
- *
- * @param timeLimit the {@code NumericProperty} with the type {@code TIME_LIMIT}
- * @see pulse.properties.NumericPropertyKeyword
- */
-
- public void setTimeLimit(NumericProperty timeLimit) {
- requireType(timeLimit, TIME_LIMIT);
- this.timeLimit = (double) timeLimit.getValue();
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- if (type == TIME_LIMIT)
- setTimeLimit(property);
- }
-
-}
\ No newline at end of file
+ private transient DiscretePulse discretePulse;
+ private Grid grid;
+
+ private double timeLimit;
+ private double pls;
+ private int timeInterval;
+
+ private static boolean hideDetailedAdjustment = true;
+
+ private final static double EPS = 1e-7; // a small value ensuring numeric stability
+
+ /**
+ * A constructor which merely sets the time limit to its default value.
+ */
+ protected DifferenceScheme() {
+ setTimeLimit(def(TIME_LIMIT));
+ }
+
+ /**
+ * A constructor for setting the time limit to a pre-set value.
+ *
+ * @param timeLimit the calculation time limit
+ */
+ protected DifferenceScheme(NumericProperty timeLimit) {
+ setTimeLimit(timeLimit);
+ }
+
+ public void initFrom(DifferenceScheme another) {
+ this.grid = grid.copy();
+ this.timeLimit = another.timeLimit;
+ this.timeInterval = another.timeInterval;
+ }
+
+ /**
+ * Copies the {@code Grid} and {@code timeLimit} from {@code df}.
+ *
+ * @param df the DifferenceScheme to copy from
+ */
+ public void copyFrom(DifferenceScheme df) {
+ this.grid = df.getGrid().copy();
+ discretePulse = null;
+ timeLimit = df.timeLimit;
+ }
+
+ /**
+ *
+ * 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) throws SolverException {
+ if (discretePulse == null) {
+ discretePulse = problem.discretePulseOn(grid);
+ }
+ discretePulse.init();
+ 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);
+ }
+
+ public void runTimeSequence(Problem problem, final double offset, final double endTime) throws SolverException {
+ var curve = problem.getHeatingCurve();
+ curve.clear();
+
+ int numPoints = (int) curve.getNumPoints().getValue();
+
+ final double startTime = (double) curve.getTimeShift().getValue();
+ final double timeSegment = (endTime - startTime - offset) / problem.getProperties().characteristicTime();
+
+ double tau = grid.getTimeStep();
+ final double dt = timeSegment / (numPoints - 1);
+ timeInterval = Math.max((int) (dt / tau), 1);
+
+ double wFactor = timeInterval * tau * problem.getProperties().characteristicTime();
+
+ // 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
+ */
+ 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.
+ */
+ timeSegment(previous, previous + timeInterval);
+ nextTime += wFactor;
+ curve.addPoint(nextTime, signal());
+ }
+
+ curve.copyToLastCalculation();
+ scaleSolution(problem);
+ }
+
+ private void timeSegment(final int m1, final int m2) throws SolverException {
+ for (int m = m1; m < m2 && normalOperation(); m++) {
+ prepareStep(m); //prepare
+ timeStep(m); //calculate
+ finaliseStep(); //finalise
+ }
+ }
+
+ public double pulse(final int m) {
+ return getDiscretePulse().laserPowerAt((m - EPS) * getGrid().getTimeStep());
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * The superclass only lists the {@code TIME_LIMIT} property.
+ */
+ @Override
+ public Set listedKeywords() {
+ var set = super.listedKeywords();
+ set.add(TIME_LIMIT);
+ return set;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+ /**
+ * Gets the discrete representation of {@code Pulse} on the {@code Grid}.
+ *
+ * @return the discrete pulse
+ * @see pulse.problem.statements.Pulse
+ */
+ public final DiscretePulse getDiscretePulse() {
+ return discretePulse;
+ }
+
+ /**
+ * Gets the {@code Grid} object defining partioning used in this
+ * {@code DifferenceScheme}
+ *
+ * @return the grid
+ */
+ public final Grid getGrid() {
+ return grid;
+ }
+
+ /**
+ * Sets the grid and adopts it as its child.
+ *
+ * @param grid the grid
+ */
+ public final void setGrid(Grid grid) {
+ this.grid = grid;
+ this.grid.setParent(this);
+ }
+
+ /**
+ * The time interval is the number of discrete timesteps that will be
+ * discarded when storing the resulting solution into a {@code HeatingCurve}
+ * object, thus ensuring that only a limited set of points is stored.
+ *
+ * @return the time interval
+ */
+ public final int getTimeInterval() {
+ return timeInterval;
+ }
+
+ /**
+ * Sets the time interval to the argument of this method.
+ *
+ * @param timeInterval a positive integer.
+ */
+ public final void setTimeInterval(int timeInterval) {
+ this.timeInterval = timeInterval;
+ }
+
+ /**
+ * If true, Lets the UI know that the user only wants to have the most
+ * important properties displayed. Otherwise this will signal all properties
+ * need to be displayed.
+ */
+ @Override
+ public final boolean areDetailsHidden() {
+ return hideDetailedAdjustment;
+ }
+
+ /**
+ * Changes the policy of displaying a detailed information about this
+ * scheme.
+ *
+ * @param b a boolean.
+ */
+ public final static void setDetailsHidden(boolean b) {
+ hideDetailedAdjustment = b;
+ }
+
+ /**
+ * The time limit (in whatever units this {@code DifferenceScheme} uses to
+ * process the solution), which serves as the ultimate breakpoint for the
+ * calculations.
+ *
+ * @return the {@code NumericProperty} with the type {@code TIME_LIMIT}
+ * @see pulse.properties.NumericPropertyKeyword
+ */
+ 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
+ * calculations.
+ *
+ * @param timeLimit the {@code NumericProperty} with the type
+ * {@code TIME_LIMIT}
+ * @see pulse.properties.NumericPropertyKeyword
+ */
+ public final void setTimeLimit(NumericProperty timeLimit) {
+ requireType(timeLimit, TIME_LIMIT);
+ this.timeLimit = (double) timeLimit.getValue();
+ firePropertyChanged(this, timeLimit);
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ if (type == TIME_LIMIT) {
+ 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();
+
+}
diff --git a/src/main/java/pulse/problem/schemes/DistributedDetection.java b/src/main/java/pulse/problem/schemes/DistributedDetection.java
index a32f06d5..3d179465 100644
--- a/src/main/java/pulse/problem/schemes/DistributedDetection.java
+++ b/src/main/java/pulse/problem/schemes/DistributedDetection.java
@@ -1,38 +1,41 @@
package pulse.problem.schemes;
+import java.io.Serializable;
import java.util.stream.IntStream;
-import pulse.problem.statements.penetration.AbsorptionModel;
-import pulse.problem.statements.penetration.SpectralRange;
+import pulse.problem.statements.model.AbsorptionModel;
+import pulse.problem.statements.model.SpectralRange;
/**
- * An interface providing the ability to calculate the integral signal
- * out from a finite-depth material layer. The depth is governed by
- * the current {@code AbsorptionModel}.
+ * An interface providing the ability to calculate the integral signal out from
+ * a finite-depth material layer. The depth is governed by the current
+ * {@code AbsorptionModel}.
*
*/
-
-public class DistributedDetection {
-
- /**
- * Calculates the effective signal registered by the detector, which takes into account
- * a distributed emission pattern. The emissivity is assumed equal to the average absorptivity
- * in the thermal region of the spectrum, as per the Kirchhoff's law.
- * @param absorption the absorption model
- * @param V the current time-temperature profile
- * @return the effective detector signal (arbitrary units)
- */
-
- public static double evaluateSignal(final AbsorptionModel absorption, final Grid grid, final double[] V) {
- final double hx = grid.getXStep();
- final int N = grid.getGridDensityValue();
-
- double signal = IntStream.range(0, N)
- .mapToDouble(i -> V[N - i] * absorption.absorption(SpectralRange.THERMAL, i * hx)
- + V[N - 1 - i] * absorption.absorption(SpectralRange.THERMAL, (i + 1) * hx))
- .reduce((a, b) -> a + b).getAsDouble();
-
- return signal * 0.5 * hx;
- }
-
-}
\ No newline at end of file
+public class DistributedDetection implements Serializable {
+
+ private static final long serialVersionUID = 3587781877001360511L;
+
+ /**
+ * Calculates the effective signal registered by the detector, which takes
+ * into account a distributed emission pattern. The emissivity is assumed
+ * equal to the average absorptivity in the thermal region of the spectrum,
+ * as per the Kirchhoff's law.
+ *
+ * @param absorption the absorption model
+ * @param V the current time-temperature profile
+ * @return the effective detector signal (arbitrary units)
+ */
+ public static double evaluateSignal(final AbsorptionModel absorption, final Grid grid, final double[] V) {
+ final double hx = grid.getXStep();
+ final int N = grid.getGridDensityValue();
+
+ double signal = IntStream.range(0, N)
+ .mapToDouble(i -> V[N - i] * absorption.absorption(SpectralRange.THERMAL, i * hx)
+ + V[N - 1 - i] * absorption.absorption(SpectralRange.THERMAL, (i + 1) * hx))
+ .reduce((a, b) -> a + b).getAsDouble();
+
+ return signal * 0.5 * hx;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/ExplicitScheme.java b/src/main/java/pulse/problem/schemes/ExplicitScheme.java
index e5fb12a4..e4b858cd 100644
--- a/src/main/java/pulse/problem/schemes/ExplicitScheme.java
+++ b/src/main/java/pulse/problem/schemes/ExplicitScheme.java
@@ -12,85 +12,85 @@
* This class provides the necessary framework to enable a simple explicit
* finite-difference scheme (also called the forward-time centred space scheme)
* for solving the one-dimensional heat conduction problem.
- *
+ *
* @see pulse.problem.statements.ClassicalProblem
* @see pulse.problem.statements.NonlinearProblem
*
*/
-
public abstract class ExplicitScheme extends OneDimensionalScheme {
- /**
- * Constructs a default explicit scheme using the default values of
- * {@code GRID_DENSITY} and {@code TAU_FACTOR}.
- */
-
- public ExplicitScheme() {
- this(derive(GRID_DENSITY, 80), derive(TAU_FACTOR, 0.5));
- }
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3025913683505686334L;
- /**
- * Constructs an explicit scheme on a one-dimensional grid that is specified by
- * the values {@code N} and {@code timeFactor}.
- *
- * @see pulse.problem.schemes.DifferenceScheme
- * @param N the {@code NumericProperty} with the type
- * {@code GRID_DENSITY}
- * @param timeFactor the {@code NumericProperty} with the type
- * {@code TAU_FACTOR}
- */
+ /**
+ * Constructs a default explicit scheme using the default values of
+ * {@code GRID_DENSITY} and {@code TAU_FACTOR}.
+ */
+ public ExplicitScheme() {
+ this(derive(GRID_DENSITY, 80), derive(TAU_FACTOR, 0.5));
+ }
- public ExplicitScheme(NumericProperty N, NumericProperty timeFactor) {
- super();
- setGrid(new Grid(N, timeFactor));
- }
+ /**
+ * Constructs an explicit scheme on a one-dimensional grid that is specified
+ * by the values {@code N} and {@code timeFactor}.
+ *
+ * @see pulse.problem.schemes.DifferenceScheme
+ * @param N the {@code NumericProperty} with the type {@code GRID_DENSITY}
+ * @param timeFactor the {@code NumericProperty} with the type
+ * {@code TAU_FACTOR}
+ */
+ public ExplicitScheme(NumericProperty N, NumericProperty timeFactor) {
+ super();
+ setGrid(new Grid(N, timeFactor));
+ }
- /**
- *
- * Constructs an explicit scheme on a one-dimensional grid that is specified by
- * the values {@code N} and {@code timeFactor}. Sets the time limit of this
- * scheme to {@code timeLimit}
- *
- * @param N the {@code NumericProperty} with the type
- * {@code GRID_DENSITY}
- * @param timeFactor the {@code NumericProperty} with the type
- * {@code TAU_FACTOR}
- * @param timeLimit the {@code NumericProperty} with the type
- * {@code TIME_LIMIT}
- * @see pulse.problem.schemes.DifferenceScheme
- */
+ /**
+ *
+ * Constructs an explicit scheme on a one-dimensional grid that is specified
+ * by the values {@code N} and {@code timeFactor}. Sets the time limit of
+ * this scheme to {@code timeLimit}
+ *
+ * @param N the {@code NumericProperty} with the type {@code GRID_DENSITY}
+ * @param timeFactor the {@code NumericProperty} with the type
+ * {@code TAU_FACTOR}
+ * @param timeLimit the {@code NumericProperty} with the type
+ * {@code TIME_LIMIT}
+ * @see pulse.problem.schemes.DifferenceScheme
+ */
+ public ExplicitScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
+ super(timeLimit);
+ setGrid(new Grid(N, timeFactor));
+ }
- public ExplicitScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
- super(timeLimit);
- setGrid(new Grid(N, timeFactor));
- }
-
- /**
- * Uses the explicit finite-difference representation of the heat equation to calculate the grid-function everywhere
- * except for the boundaries. This will update the current solution using the solution from previous time step.
- */
-
- public void explicitSolution() {
- var grid = getGrid();
- var U = getPreviousSolution();
- final double TAU_HH = grid.getTimeStep()/(fastPowLoop(grid.getXStep(), 2));
- for (int i = 1, N = grid.getGridDensityValue(); i < N; i++)
- setSolutionAt(i, U[i] + TAU_HH * (U[i + 1] - 2. * U[i] + U[i - 1]) + phi(i) );
- }
-
- public double phi(final int i) {
- return 0;
- }
+ /**
+ * Uses the explicit finite-difference representation of the heat equation
+ * to calculate the grid-function everywhere except for the boundaries. This
+ * will update the current solution using the solution from previous time
+ * step.
+ */
+ public void explicitSolution() {
+ var grid = getGrid();
+ var U = getPreviousSolution();
+ final double TAU_HH = grid.getTimeStep() / (fastPowLoop(grid.getXStep(), 2));
+ for (int i = 1, N = grid.getGridDensityValue(); i < N; i++) {
+ setSolutionAt(i, U[i] + TAU_HH * (U[i + 1] - 2. * U[i] + U[i - 1]) + phi(i));
+ }
+ }
- /**
- * Prints out the description of this problem type.
- *
- * @return a verbose description of the problem.
- */
+ public double phi(final int i) {
+ return 0;
+ }
- @Override
- public String toString() {
- return getString("ExplicitScheme.4");
- }
+ /**
+ * Prints out the description of this problem type.
+ *
+ * @return a verbose description of the problem.
+ */
+ @Override
+ public String toString() {
+ return getString("ExplicitScheme.4");
+ }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/pulse/problem/schemes/FixedPointIterations.java b/src/main/java/pulse/problem/schemes/FixedPointIterations.java
index a093e67f..dbf5992f 100644
--- a/src/main/java/pulse/problem/schemes/FixedPointIterations.java
+++ b/src/main/java/pulse/problem/schemes/FixedPointIterations.java
@@ -2,26 +2,70 @@
import static java.lang.Math.abs;
-public interface FixedPointIterations {
+import java.io.Serializable;
+import java.util.Arrays;
+import pulse.problem.schemes.solvers.SolverException;
+import static pulse.problem.schemes.solvers.SolverException.SolverExceptionType.FINITE_DIFFERENCE_ERROR;
- public default void doIterations(double[] V, final double error, final int m) {
+/**
+ * @see Wiki
+ * page
+ *
+ */
+public interface FixedPointIterations extends Serializable {
- final int N = V.length - 1;
+ /**
+ * Performs iterations until the convergence criterion is satisfied.The
+ * latter consists in having a difference two consequent iterations of V
+ * less than the specified error. At the end of each iteration, calls
+ * {@code finaliseIteration()}.
+ *
+ * @param V the calculation array
+ * @param error used in the convergence criterion
+ * @param m time step
+ * @throws pulse.problem.schemes.solvers.SolverException if the calculation
+ * failed
+ * @see finaliseIteration()
+ * @see iteration()
+ */
+ public default void doIterations(double[] V, final double error, final int m) throws SolverException {
- for (double V_0 = error + 1, V_N = error + 1; abs(V[0] - V_0) > error
- || abs(V[N] - V_N) > error; finaliseIteration(V)) {
+ final int N = V.length - 1;
- V_N = V[N];
- V_0 = V[0];
- iteration(m);
+ for (double V_0 = error + 1, V_N = error + 1;
+ abs(V[0] - V_0) / abs(V[0] + V_0 + 1e-16) > error
+ || abs(V[N] - V_N) / abs(V[N] + V_N + 1e-16) > error; finaliseIteration(V)) {
- }
- }
+ V_N = V[N];
+ V_0 = V[0];
+ iteration(m);
- public void iteration(final int m);
+ }
+ }
- public default void finaliseIteration(double[] V) {
- // do nothing
- }
+ /**
+ * Performs an iteration at time {@code m}
+ *
+ * @param m time step
+ * @throws pulse.problem.schemes.solvers.SolverException if the calculation
+ * failed
+ */
+ public void iteration(final int m) throws SolverException;
-}
\ No newline at end of file
+ /**
+ * Finalises the current iteration.By default, does nothing.
+ *
+ * @param V the current iteration
+ * @throws pulse.problem.schemes.solvers.SolverException if the calculation
+ * failed
+ */
+ public default void finaliseIteration(double[] V) throws SolverException {
+ final double threshold = 1E6;
+ double sum = Arrays.stream(V).sum();
+ if (sum > threshold || !Double.isFinite(sum)) {
+ throw new SolverException("Invalid solution values in V array",
+ FINITE_DIFFERENCE_ERROR);
+ }
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/Grid.java b/src/main/java/pulse/problem/schemes/Grid.java
index 20537fb8..d8a0b331 100644
--- a/src/main/java/pulse/problem/schemes/Grid.java
+++ b/src/main/java/pulse/problem/schemes/Grid.java
@@ -2,20 +2,15 @@
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 pulse.properties.Property;
import pulse.util.PropertyHolder;
/**
@@ -28,218 +23,196 @@
*
*
*/
-
public class Grid extends PropertyHolder {
- private double hx;
- private double tau;
- private double tauFactor;
- private int N;
-
- /**
- * Creates a {@code Grid} object with the specified {@code gridDensity} and
- * {@code timeFactor}.
- *
- * @param gridDensity a {@code NumericProperty} of the type {@code GRID_DENSITY}
- * @param timeFactor a {@code NumericProperty} of the type {@code TIME_FACTOR}
- * @see pulse.properties.NumericPropertyKeyword
- */
-
- public Grid(NumericProperty gridDensity, NumericProperty timeFactor) {
- setGridDensity(gridDensity);
- setTimeFactor(timeFactor);
- }
-
- protected Grid() {
- // intentionally blank
- }
-
- /**
- * Creates a new {@code Grid} object with exactly the same parameters as this
- * one.
- *
- * @return a new {@code Grid} object replicating this {@code Grid}
- */
-
- public Grid copy() {
- return new Grid(getGridDensity(), getTimeFactor());
- }
-
- /**
- * Optimises the {@code Grid} parameters.
- *
- * This can change the {@code tauFactor} and {@code tau} variables in the
- * {@code Grid} object if {@code discretePulseWidth < grid.tau}.
- *
- *
- * @param pulse the discrete pulse representation
- */
-
- 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);
- }
- }
-
- /**
- * The listed properties include {@code GRID_DENSITY} and {@code TAU_FACTOR}.
- */
-
- @Override
- public List listedTypes() {
- List list = new ArrayList<>(2);
- list.add(def(GRID_DENSITY));
- list.add(def(TAU_FACTOR));
- return list;
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- switch (type) {
- case TAU_FACTOR:
- setTimeFactor(property);
- break;
- case GRID_DENSITY:
- setGridDensity(property);
- break;
- default:
- break;
- }
- }
-
- /**
- * Retrieves the value of the coordinate step
- * used in finite-difference calculation.
- *
- * @return a double, representing the {@code hx} value.
- */
-
- public double getXStep() {
- return hx;
- }
-
- /**
- * Sets the value of the coordinate step.
- *
- * @param hx a double, representing the new {@code hx} value.
- */
-
- public void setXStep(double hx) {
- this.hx = hx;
- }
-
- /**
- * Retrieves the value of the τ time step used in finite-difference
- * calculation.
- *
- * @return a double, representing the {@code tau} value.
- */
-
- public double getTimeStep() {
- return tau;
- }
-
- protected void setTimeStep(double tau) {
- this.tau = tau;
- }
-
- /**
- * Retrieves the value of the τ-factor, or the time factor, used in
- * finite-difference calculation. This factor determines the proportionally
- * coefficient between τ and .
- *
- * @return a NumericProperty of the {@code TAU_FACTOR} type, representing the
- * {@code tauFactor} value.
- */
-
- public NumericProperty getTimeFactor() {
- return derive(TAU_FACTOR, tauFactor);
- }
-
- /**
- * Retrieves the value of the {@code gridDensity} used to calculate the
- * {@code hx} and {@code tau}.
- *
- * @return a NumericProperty of the {@code GRID_DENSITY} type, representing the
- * {@code gridDensity} value.
- */
-
- public NumericProperty getGridDensity() {
- return derive(GRID_DENSITY, N);
- }
-
- protected int getGridDensityValue() {
- return N;
- }
-
- protected void setGridDensityValue(int N) {
- this.N = N;
- }
-
- /**
- * Sets the value of the {@code gridDensity}. Automatically recalculates the
- * {@code hx} value.
- *
- * @param gridDensity a NumericProperty of the {@code GRID_DENSITY} type
- */
-
- public void setGridDensity(NumericProperty gridDensity) {
- requireType(gridDensity, GRID_DENSITY);
- this.N = (int) gridDensity.getValue();
- hx = 1. / N;
- setTimeFactor(derive(TAU_FACTOR, 1.0));
- }
-
- /**
- * Sets the value of the {@code tauFactor}. Automatically recalculates the
- * {@code tau} value.
- *
- * @param timeFactor a NumericProperty of the {@code TAU_FACTOR} type
- */
-
- public void setTimeFactor(NumericProperty timeFactor) {
- requireType(timeFactor, TAU_FACTOR);
- this.tauFactor = (double) timeFactor.getValue();
- setTimeStep(tauFactor * pow(hx, 2));
- }
-
- /**
- * The dimensionless time on this {@code Grid}, which is the
- * {@code time/dimensionFactor} rounded up to a factor of the time step
- * {@code tau}.
- *
- * @param time the time
- * @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) {
- return rint((time / dimensionFactor) / tau) * tau;
- }
-
- /**
- * The dimensionless axial distance on this {@code Grid}, which is the
- * {@code distance/lengthFactor} rounded up to a factor of the coordinate step
- * {@code hx}.
- *
- * @param distance the distance along the axial direction
- * @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) {
- return rint((distance / lengthFactor) / hx) * hx;
- }
-
- @Override
- public String toString() {
- var sb = new StringBuilder();
- sb.append("");
- sb.append(getClass().getSimpleName() + ":
*/
-
public class Grid2D extends Grid {
- private double hy;
-
- protected Grid2D() {
- super();
- }
-
- /**
- * Creates a {@code Grid2D} where the radial and axial spatial steps are equal
- * to the inverse {@code gridDensity}. Otherwise, calls the superclass
- * constructor.
- *
- * @param gridDensity the grid density
- * @param timeFactor the {@code τhy=" + format("%3.3f", hy));
- return sb.toString();
- }
-
- public double getYStep() {
- return hy;
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = 564113358979595637L;
+ private double hy;
+
+ protected Grid2D() {
+ super();
+ }
+
+ /**
+ * Creates a {@code Grid2D} where the radial and axial spatial steps are
+ * equal to the inverse {@code gridDensity}. Otherwise, calls the superclass
+ * constructor.
+ *
+ * @param gridDensity the grid density
+ * @param timeFactor the {@code τF} factor
+ */
+ public Grid2D(NumericProperty gridDensity, NumericProperty timeFactor) {
+ super(gridDensity, timeFactor);
+ hy = 1.0 / getGridDensityValue();
+ }
+
+ @Override
+ public Grid2D copy() {
+ return new Grid2D(getGridDensity(), getTimeFactor());
+ }
+
+ @Override
+ public void setTimeFactor(NumericProperty timeFactor) {
+ super.setTimeFactor(timeFactor);
+ setTimeStep((double) timeFactor.getValue() * (pow(getXStep(), 2) + pow(hy, 2)));
+ }
+
+ /**
+ * Calls the {@code adjustTo} method from superclass, then adjusts the
+ * {@code gridDensity} of the {@code grid} if
+ * {@code discretePulseSpot < (Grid2D)grid.hy}.
+ *
+ * @param pulse the discrete puls representation
+ */
+ 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);
+ }
+
+ }
+
+ @Override
+ protected void setGridDensityValue(int N) {
+ super.setGridDensityValue(N);
+ hy = 1. / N;
+ }
+
+ /**
+ * Sets the value of the {@code gridDensity}. Automatically recalculates the
+ * {@code hx} an {@code hy} values.
+ */
+ @Override
+ public void setGridDensity(NumericProperty gridDensity) {
+ super.setGridDensity(gridDensity);
+ hy = getXStep();
+ }
+
+ /**
+ * The dimensionless radial distance on this {@code Grid2D}, which is the
+ * {@code radial/lengthFactor} rounded up to a factor of the coordinate step
+ * {@code hy}.
+ *
+ * @param radial the distance along the radial direction
+ * @param lengthFactor a factor which has the dimension of length
+ * @return a double representing the radial distance on the finite grid
+ */
+ public double gridRadialDistance(double radial, double lengthFactor) {
+ return rint((radial / lengthFactor) / hy) * hy;
+ }
+
+ public double getYStep() {
+ return hy;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/ImplicitScheme.java b/src/main/java/pulse/problem/schemes/ImplicitScheme.java
index af2324c6..0d72003c 100644
--- a/src/main/java/pulse/problem/schemes/ImplicitScheme.java
+++ b/src/main/java/pulse/problem/schemes/ImplicitScheme.java
@@ -1,5 +1,6 @@
package pulse.problem.schemes;
+import pulse.problem.schemes.solvers.SolverException;
import static pulse.properties.NumericProperties.derive;
import static pulse.properties.NumericPropertyKeyword.GRID_DENSITY;
import static pulse.properties.NumericPropertyKeyword.TAU_FACTOR;
@@ -11,100 +12,108 @@
/**
* An abstract implicit finite-difference scheme for solving one-dimensional
* heat conduction problems.
- *
+ *
* @see pulse.problem.statements.ClassicalProblem
* @see pulse.problem.statements.NonlinearProblem
*/
-
public abstract class ImplicitScheme extends OneDimensionalScheme {
-
- private TridiagonalMatrixAlgorithm tridiagonal;
-
- /**
- * Constructs a default fully-implicit scheme using the default values of
- * {@code GRID_DENSITY} and {@code TAU_FACTOR}.
- */
-
- public ImplicitScheme() {
- this(derive(GRID_DENSITY, 30), derive(TAU_FACTOR, 0.25));
- }
-
- /**
- * Constructs a fully-implicit scheme on a one-dimensional grid that is
- * specified by the values {@code N} and {@code timeFactor}.
- *
- * @see pulse.problem.schemes.DifferenceScheme
- * @param N the {@code NumericProperty} with the type
- * {@code GRID_DENSITY}
- * @param timeFactor the {@code NumericProperty} with the type
- * {@code TAU_FACTOR}
- */
-
- public ImplicitScheme(NumericProperty N, NumericProperty timeFactor) {
- super();
- setGrid(new Grid(N, timeFactor));
- }
-
- /**
- *
- * Constructs a fully-implicit scheme on a one-dimensional grid that is
- * specified by the values {@code N} and {@code timeFactor}. Sets the time limit
- * of this scheme to {@code timeLimit}
- *
- * @param N the {@code NumericProperty} with the type
- * {@code GRID_DENSITY}
- * @param timeFactor the {@code NumericProperty} with the type
- * {@code TAU_FACTOR}
- * @param timeLimit the {@code NumericProperty} with the type
- * {@code TIME_LIMIT}
- * @see pulse.problem.schemes.DifferenceScheme
- */
-
- public ImplicitScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
- super(timeLimit);
- setGrid(new Grid(N, timeFactor));
- }
-
- @Override
- protected void prepare(Problem problem) {
- super.prepare(problem);
- tridiagonal = new TridiagonalMatrixAlgorithm(getGrid());
- }
-
- @Override
- public void timeStep(final int m) {
- leftBoundary(m);
- final var V = getCurrentSolution();
- final int N = V.length - 1;
- setSolutionAt(N, evalRightBoundary(m, tridiagonal.getAlpha()[N], tridiagonal.getBeta()[N]) );
- tridiagonal.sweep(V);
- }
-
- public void leftBoundary(final int m) {
- tridiagonal.setBeta( 1, firstBeta(m) );
- tridiagonal.evaluateBeta(getPreviousSolution());
- }
-
- public abstract double evalRightBoundary(final int m, final double alphaN, final double betaN);
- public abstract double firstBeta(final int m);
-
- /**
- * Prints out the description of this problem type.
- *
- * @return a verbose description of the problem.
- */
-
- @Override
- public String toString() {
- return getString("ImplicitScheme.4");
- }
-
- public TridiagonalMatrixAlgorithm getTridiagonalMatrixAlgorithm() {
- return tridiagonal;
- }
-
- public void setTridiagonalMatrixAlgorithm(TridiagonalMatrixAlgorithm tridiagonal) {
- this.tridiagonal = tridiagonal;
- }
-
-}
\ No newline at end of file
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2785615380656900783L;
+ private TridiagonalMatrixAlgorithm tridiagonal;
+
+ /**
+ * Constructs a default fully-implicit scheme using the default values of
+ * {@code GRID_DENSITY} and {@code TAU_FACTOR}.
+ */
+ public ImplicitScheme() {
+ this(derive(GRID_DENSITY, 30), derive(TAU_FACTOR, 0.25));
+ }
+
+ /**
+ * Constructs a fully-implicit scheme on a one-dimensional grid that is
+ * specified by the values {@code N} and {@code timeFactor}.
+ *
+ * @see pulse.problem.schemes.DifferenceScheme
+ * @param N the {@code NumericProperty} with the type {@code GRID_DENSITY}
+ * @param timeFactor the {@code NumericProperty} with the type
+ * {@code TAU_FACTOR}
+ */
+ public ImplicitScheme(NumericProperty N, NumericProperty timeFactor) {
+ super();
+ setGrid(new Grid(N, timeFactor));
+ }
+
+ /**
+ *
+ * Constructs a fully-implicit scheme on a one-dimensional grid that is
+ * specified by the values {@code N} and {@code timeFactor}. Sets the time
+ * limit of this scheme to {@code timeLimit}
+ *
+ * @param N the {@code NumericProperty} with the type {@code GRID_DENSITY}
+ * @param timeFactor the {@code NumericProperty} with the type
+ * {@code TAU_FACTOR}
+ * @param timeLimit the {@code NumericProperty} with the type
+ * {@code TIME_LIMIT}
+ * @see pulse.problem.schemes.DifferenceScheme
+ */
+ public ImplicitScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
+ super(timeLimit);
+ setGrid(new Grid(N, timeFactor));
+ }
+
+ @Override
+ protected void prepare(Problem problem) throws SolverException {
+ super.prepare(problem);
+ tridiagonal = new TridiagonalMatrixAlgorithm(getGrid());
+ }
+
+ /**
+ * Calculates the solution at the boundaries using the boundary conditions
+ * specific to the problem statement and runs the tridiagonal matrix
+ * algorithm to evaluate solution at the intermediate grid points.
+ *
+ * @param m the time step
+ * @throws SolverException if the calculation failed
+ * @see leftBoundary(), evalRightBoundary(),
+ * pulse.problem.schemes.TridiagonalMatrixAlgorithm.sweep()
+ */
+ @Override
+ public void timeStep(final int m) throws SolverException {
+ leftBoundary(m);
+ final var V = getCurrentSolution();
+ final int N = V.length - 1;
+ setSolutionAt(N, evalRightBoundary(tridiagonal.getAlpha()[N], tridiagonal.getBeta()[N]));
+ tridiagonal.sweep(V);
+ }
+
+ public void leftBoundary(int m) {
+ tridiagonal.setBeta(1, firstBeta());
+ tridiagonal.evaluateBeta(getPreviousSolution());
+ }
+
+ public abstract double evalRightBoundary(final double alphaN, final double betaN);
+
+ public abstract double firstBeta();
+
+ /**
+ * Prints out the description of this problem type.
+ *
+ * @return a verbose description of the problem.
+ */
+ @Override
+ public String toString() {
+ return getString("ImplicitScheme.4");
+ }
+
+ public TridiagonalMatrixAlgorithm getTridiagonalMatrixAlgorithm() {
+ return tridiagonal;
+ }
+
+ public void setTridiagonalMatrixAlgorithm(TridiagonalMatrixAlgorithm tridiagonal) {
+ this.tridiagonal = tridiagonal;
+ }
+
+}
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 68f2c8e3..00000000
--- a/src/main/java/pulse/problem/schemes/LayeredGrid2D.java
+++ /dev/null
@@ -1,83 +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 pulse.problem.schemes.Partition.Location;
-import pulse.properties.NumericProperty;
-import pulse.properties.NumericPropertyKeyword;
-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 List listedTypes() {
- List list = new ArrayList<>(2);
- list.add(def(SHELL_GRID_DENSITY));
- return list;
- }
-
- @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);
- }
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/pulse/problem/schemes/MixedScheme.java b/src/main/java/pulse/problem/schemes/MixedScheme.java
index 976469b2..fa53e697 100644
--- a/src/main/java/pulse/problem/schemes/MixedScheme.java
+++ b/src/main/java/pulse/problem/schemes/MixedScheme.java
@@ -10,66 +10,64 @@
/**
* An abstraction describing a weighted semi-implicit finite-difference scheme
* for solving the one-dimensional heat conduction problem.
- *
+ *
* @see pulse.problem.statements.ClassicalProblem
* @see pulse.problem.statements.NonlinearProblem
*
*/
-
public abstract class MixedScheme extends ImplicitScheme {
- /**
- * Constructs a default semi-implicit scheme using the default values of
- * {@code GRID_DENSITY} and {@code TAU_FACTOR}.
- */
-
- public MixedScheme() {
- this(derive(GRID_DENSITY, 30), derive(TAU_FACTOR, 1.0));
- }
-
- /**
- * Constructs a semi-implicit scheme on a one-dimensional grid that is specified
- * by the values {@code N} and {@code timeFactor}.
- *
- * @see pulse.problem.schemes.DifferenceScheme
- * @param N the {@code NumericProperty} with the type
- * {@code GRID_DENSITY}
- * @param timeFactor the {@code NumericProperty} with the type
- * {@code TAU_FACTOR}
- */
-
- public MixedScheme(NumericProperty N, NumericProperty timeFactor) {
- super(N, timeFactor);
- }
+ /**
+ *
+ */
+ private static final long serialVersionUID = -770528855578192638L;
- /**
- *
- * Constructs a semi-implicit scheme on a one-dimensional grid that is specified
- * by the values {@code N} and {@code timeFactor}. Sets the time limit of this
- * scheme to {@code timeLimit}
- *
- * @param N the {@code NumericProperty} with the type
- * {@code GRID_DENSITY}
- * @param timeFactor the {@code NumericProperty} with the type
- * {@code TAU_FACTOR}
- * @param timeLimit the {@code NumericProperty} with the type
- * {@code TIME_LIMIT}
- * @see pulse.problem.schemes.DifferenceScheme
- */
+ /**
+ * Constructs a default semi-implicit scheme using the default values of
+ * {@code GRID_DENSITY} and {@code TAU_FACTOR}.
+ */
+ public MixedScheme() {
+ this(derive(GRID_DENSITY, 30), derive(TAU_FACTOR, 1.0));
+ }
- public MixedScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
- super(N, timeFactor, timeLimit);
- }
+ /**
+ * Constructs a semi-implicit scheme on a one-dimensional grid that is
+ * specified by the values {@code N} and {@code timeFactor}.
+ *
+ * @see pulse.problem.schemes.DifferenceScheme
+ * @param N the {@code NumericProperty} with the type {@code GRID_DENSITY}
+ * @param timeFactor the {@code NumericProperty} with the type
+ * {@code TAU_FACTOR}
+ */
+ public MixedScheme(NumericProperty N, NumericProperty timeFactor) {
+ super(N, timeFactor);
+ }
- /**
- * Prints out the description of this problem type.
- *
- * @return a verbose description of the problem.
- */
+ /**
+ *
+ * Constructs a semi-implicit scheme on a one-dimensional grid that is
+ * specified by the values {@code N} and {@code timeFactor}. Sets the time
+ * limit of this scheme to {@code timeLimit}
+ *
+ * @param N the {@code NumericProperty} with the type {@code GRID_DENSITY}
+ * @param timeFactor the {@code NumericProperty} with the type
+ * {@code TAU_FACTOR}
+ * @param timeLimit the {@code NumericProperty} with the type
+ * {@code TIME_LIMIT}
+ * @see pulse.problem.schemes.DifferenceScheme
+ */
+ public MixedScheme(NumericProperty N, NumericProperty timeFactor, NumericProperty timeLimit) {
+ super(N, timeFactor, timeLimit);
+ }
- @Override
- public String toString() {
- return getString("MixedScheme.4");
- }
+ /**
+ * Prints out the description of this problem type.
+ *
+ * @return a verbose description of the problem.
+ */
+ @Override
+ public String toString() {
+ return getString("MixedScheme.4");
+ }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java b/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java
index 12a7f756..b58bf0fe 100644
--- a/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java
+++ b/src/main/java/pulse/problem/schemes/OneDimensionalScheme.java
@@ -1,49 +1,54 @@
package pulse.problem.schemes;
-import pulse.problem.statements.Problem;
+import pulse.problem.schemes.solvers.SolverException;
import pulse.properties.NumericProperty;
public abstract class OneDimensionalScheme extends DifferenceScheme {
- private double[] U;
- private double[] V;
-
- protected OneDimensionalScheme() {
- super();
- }
-
- protected OneDimensionalScheme(NumericProperty timeLimit) {
- super(timeLimit);
- }
-
- @Override
- protected void prepare(Problem problem) {
- super.prepare(problem);
- final int N = (int) getGrid().getGridDensity().getValue();
- U = new double[N + 1];
- V = new double[N + 1];
- }
-
- @Override
- public double signal() {
- return V[ V.length - 1 ];
- }
-
- @Override
- public void finaliseStep() {
- System.arraycopy(V, 0, U, 0, V.length);
- }
-
- public double[] getPreviousSolution() {
- return U;
- }
-
- public double[] getCurrentSolution() {
- return V;
- }
-
- public void setSolutionAt(final int i, final double v) {
- this.V[i] = v;
- }
-
-}
\ No newline at end of file
+ private double[] U;
+ private double[] V;
+
+ protected OneDimensionalScheme() {
+ super();
+ }
+
+ protected OneDimensionalScheme(NumericProperty timeLimit) {
+ super(timeLimit);
+ }
+
+ @Override
+ public void clearArrays() {
+ final int N = (int) getGrid().getGridDensity().getValue();
+ U = new double[N + 1];
+ V = new double[N + 1];
+ }
+
+ @Override
+ public double signal() {
+ return V[V.length - 1];
+ }
+
+ /**
+ * Overwrites previously calculated temperature values with the calculations
+ * made at the current time step
+ *
+ * @throws SolverException if the calculation failed
+ */
+ @Override
+ public void finaliseStep() throws SolverException {
+ System.arraycopy(V, 0, U, 0, V.length);
+ }
+
+ public double[] getPreviousSolution() {
+ return U;
+ }
+
+ public double[] getCurrentSolution() {
+ return V;
+ }
+
+ public void setSolutionAt(final int i, final double v) {
+ this.V[i] = v;
+ }
+
+}
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 8fe29b6d..00000000
--- 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);
- }
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java b/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java
index 8208c939..e507e00d 100644
--- a/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java
+++ b/src/main/java/pulse/problem/schemes/RadiativeTransferCoupling.java
@@ -1,12 +1,12 @@
package pulse.problem.schemes;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import pulse.problem.schemes.rte.RadiativeTransferSolver;
import pulse.problem.schemes.rte.dom.DiscreteOrdinatesMethod;
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;
@@ -14,67 +14,76 @@
import pulse.util.PropertyHolder;
public class RadiativeTransferCoupling extends PropertyHolder {
-
- private RadiativeTransferSolver rte;
- private InstanceDescriptor extends RadiativeTransferSolver> instanceDescriptor = new InstanceDescriptor(
- "RTE Solver Selector", RadiativeTransferSolver.class);
-
- public RadiativeTransferCoupling() {
- instanceDescriptor.setSelectedDescriptor(DiscreteOrdinatesMethod.class.getSimpleName());
- instanceDescriptor.addListener(() -> firePropertyChanged(this, instanceDescriptor));
- super.parameterListChanged();
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- //intentionally blank
- }
-
- public void init(ParticipatingMedium problem, Grid grid) {
-
- if (rte == null) {
- newRTE(problem, grid);
- instanceDescriptor.addListener(() -> {
- newRTE(problem, grid);
- rte.init(problem, grid);
- });
-
- }
- else
- rte.init(problem, grid);
-
- }
-
- private void newRTE(ParticipatingMedium problem, Grid grid) {
- rte = instanceDescriptor.newInstance(RadiativeTransferSolver.class, problem, grid);
- rte.setParent(this);
- }
-
- public InstanceDescriptor extends RadiativeTransferSolver> getInstanceDescriptor() {
- return instanceDescriptor;
- }
-
- public RadiativeTransferSolver getRadiativeTransferEquation() {
- return rte;
- }
-
- public void setRadiativeTransferEquation(RadiativeTransferSolver solver) {
- this.rte = solver;
- }
-
- @Override
- public String toString() {
- return instanceDescriptor.toString();
- }
-
- @Override
- public String getPrefix() {
- return "RTE Coupling";
- }
-
- @Override
- public List listedTypes() {
- return new ArrayList(Arrays.asList(instanceDescriptor));
- }
-
-}
\ No newline at end of file
+
+ private static final long serialVersionUID = -8969606772435213260L;
+ private RadiativeTransferSolver rte;
+ private InstanceDescriptor extends RadiativeTransferSolver> instanceDescriptor
+ = new InstanceDescriptor(
+ "RTE Solver Selector", RadiativeTransferSolver.class);
+
+ public RadiativeTransferCoupling() {
+ instanceDescriptor.setSelectedDescriptor(DiscreteOrdinatesMethod.class.getSimpleName());
+ instanceDescriptor.addListener(() -> firePropertyChanged(this, instanceDescriptor));
+ super.parameterListChanged();
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ //intentionally blank
+ }
+
+ 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(Problem problem, Grid grid) {
+ rte = instanceDescriptor.newInstance(RadiativeTransferSolver.class, problem, grid);
+ rte.setParent(this);
+ }
+
+ public InstanceDescriptor extends RadiativeTransferSolver> getInstanceDescriptor() {
+ return instanceDescriptor;
+ }
+
+ public RadiativeTransferSolver getRadiativeTransferEquation() {
+ return rte;
+ }
+
+ public void setRadiativeTransferEquation(RadiativeTransferSolver solver) {
+ this.rte = solver;
+ }
+
+ @Override
+ public String toString() {
+ return instanceDescriptor.toString();
+ }
+
+ @Override
+ public String getPrefix() {
+ return "RTE Coupling";
+ }
+
+ @Override
+ public List listedTypes() {
+ var list = super.listedTypes();
+ list.add(instanceDescriptor);
+ return list;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java b/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java
index 01c3c8cb..0b76d966 100644
--- a/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java
+++ b/src/main/java/pulse/problem/schemes/TridiagonalMatrixAlgorithm.java
@@ -1,116 +1,137 @@
package pulse.problem.schemes;
+import java.io.Serializable;
+
/**
- * Implements the tridiagonal matrix algorithm (Thomas algorithms) for solving systems of linear equations.
- * Applicable to such systems where the forming matrix has a tridiagonal form.
+ * Implements the tridiagonal matrix algorithm (Thomas algorithms) for solving
+ * systems of linear equations. Applicable to such systems where the forming
+ * matrix has a tridiagonal form: Ai*xi-1 - Bi
+ * xi + Ci xi+1 = -Fi.
*
*/
+public class TridiagonalMatrixAlgorithm implements Serializable {
+
+ private static final long serialVersionUID = 8201903787985856087L;
+ private final double tau;
+ private final double h;
+
+ private double a;
+ private double b;
+ private double c;
+
+ private final int N;
+ private final double[] alpha;
+ private final double[] beta;
+
+ public TridiagonalMatrixAlgorithm(Grid grid) {
+ tau = grid.getTimeStep();
+ N = grid.getGridDensityValue();
+ h = grid.getXStep();
+ alpha = new double[N + 2];
+ beta = new double[N + 2];
+ }
+
+ /**
+ * Calculates the solution {@code V} using the tridiagonal matrix algorithm.
+ * This performs a backwards sweep from {@code N - 1} to {@code 0} where
+ * {@code N} is the grid density value. The coefficients {@code alpha} and
+ * {@code beta} should have been precalculated
+ *
+ * @param V the array containing the {@code N}th value previously calculated
+ * from the respective boundary condition
+ */
+ public void sweep(double[] V) {
+ for (int j = N - 1; j >= 0; j--) {
+ V[j] = alpha[j + 1] * V[j + 1] + beta[j + 1];
+ }
+ }
+
+ /**
+ * Calculates the {@code alpha} coefficients as part of the tridiagonal
+ * matrix algorithm.
+ */
+ public void evaluateAlpha() {
+ for (int i = 1; i < N; i++) {
+ alpha[i + 1] = c / (b - a * alpha[i]);
+ }
+ }
+
+ public void evaluateBeta(final double[] U) {
+ 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) {
+ for (int i = start; i < endExclusive; i++) {
+ beta[i] = beta(U[i - 1] / tau, phi(i - 1), i);
+ }
+ }
+
+ public double beta(final double f, final double phi, final int i) {
+ return (f + phi + a * beta[i - 1]) / (b - a * alpha[i - 1]);
+ }
+
+ public double phi(int i) {
+ return 0;
+ }
+
+ public void setAlpha(final int i, final double alpha) {
+ this.alpha[i] = alpha;
+ }
+
+ public void setBeta(final int i, final double beta) {
+ this.beta[i] = beta;
+ }
+
+ public double[] getAlpha() {
+ return alpha;
+ }
+
+ public double[] getBeta() {
+ return beta;
+ }
+
+ public void setCoefA(double a) {
+ this.a = a;
+ }
+
+ public void setCoefB(double b) {
+ this.b = b;
+ }
+
+ public void setCoefC(double c) {
+ this.c = c;
+ }
+
+ protected double getCoefA() {
+ return a;
+ }
+
+ protected double getCoefB() {
+ return b;
+ }
+
+ protected double getCoefC() {
+ return c;
+ }
+
+ public final double getTimeStep() {
+ return tau;
+ }
+
+ public final int getGridPoints() {
+ return N;
+ }
+
+ public final double getGridStep() {
+ return h;
+ }
-public class TridiagonalMatrixAlgorithm {
-
- private Grid grid;
-
- private double a;
- private double b;
- private double c;
-
- private double[] alpha;
- private double[] beta;
-
- public TridiagonalMatrixAlgorithm(Grid grid) {
- this.grid = grid;
- final int N = grid.getGridDensityValue();
- alpha = new double[N + 2];
- beta = new double[N + 2];
- }
-
- /**
- * Calculates the solution {@code V} using the tridiagonal matrix algorithm.
- * This performs a backwards sweep from {@code N - 1} to {@code 0} where {@code N}
- * is the grid density value. The coefficients {@code alpha} and {@code beta}
- * should have been precalculated
- * @param V the array containing the {@code N}th value previously calculated from the respective boundary condition
- */
-
- public void sweep(double[] V) {
- for (int j = grid.getGridDensityValue() - 1; j >= 0; j--)
- V[j] = alpha[j + 1] * V[j + 1] + beta[j + 1];
- }
-
- /**
- * Calculates the {@code alpha} coefficients as part of the tridiagonal matrix algorithm.
- */
-
- public void evaluateAlpha() {
- for (int i = 1, N = grid.getGridDensityValue(); i < N; i++) {
- alpha[i + 1] = c / (b - a * alpha[i]);
- }
- }
-
- public void evaluateBeta(final double[] U) {
- evaluateBeta(U, 2, grid.getGridDensityValue() + 1);
- }
-
- /**
- * Calculates the {@code beta} coefficients as part of the tridiagonal matrix algorithm.
- */
-
- 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);
- }
-
- public double beta(final double f, final double phi, final int i) {
- return (f + phi + a * beta[i - 1]) / (b - a * alpha[i - 1]);
- }
-
- public double phi(int i) {
- return 0;
- }
-
- public void setAlpha(final int i, final double alpha) {
- this.alpha[i] = alpha;
- }
-
- public void setBeta(final int i, final double beta) {
- this.beta[i] = beta;
- }
-
- public double[] getAlpha() {
- return alpha;
- }
-
- public double[] getBeta() {
- return beta;
- }
-
- public void setCoefA(double a) {
- this.a = a;
- }
-
- public void setCoefB(double b) {
- this.b = b;
- }
-
- public void setCoefC(double c) {
- this.c = c;
- }
-
- protected double getCoefA() {
- return a;
- }
-
- protected double getCoefB() {
- return b;
- }
-
- protected double getCoefC() {
- return c;
- }
-
- public Grid getGrid() {
- return grid;
- }
-
-}
\ No newline at end of file
+}
diff --git a/src/main/java/pulse/problem/schemes/package-info.java b/src/main/java/pulse/problem/schemes/package-info.java
index 5ab514ab..2b28afa7 100644
--- a/src/main/java/pulse/problem/schemes/package-info.java
+++ b/src/main/java/pulse/problem/schemes/package-info.java
@@ -3,8 +3,7 @@
* PULsE, including the definition of {@code Grid}s, which determine the
* partitioning rules for space and time variables. Specific implementation of
* the difference schemes may be found separately in a different package.
- *
+ *
* @see pulse.problem.schemes.solvers.Solver
*/
-
-package pulse.problem.schemes;
\ No newline at end of file
+package pulse.problem.schemes;
diff --git a/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java b/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java
index 7aaa1782..5f65f01e 100644
--- a/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java
+++ b/src/main/java/pulse/problem/schemes/rte/BlackbodySpectrum.java
@@ -1,11 +1,15 @@
package pulse.problem.schemes.rte;
-import static pulse.math.MathUtils.fastPowLoop;
-
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
import org.apache.commons.math3.analysis.UnivariateFunction;
-
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+import static pulse.math.MathUtils.fastPowLoop;
import pulse.problem.statements.NonlinearProblem;
import pulse.problem.statements.Pulse2D;
+import pulse.util.FunctionSerializer;
/**
* Contains methods for calculating the integral spectral characteristics of a
@@ -14,77 +18,99 @@
* {@code SplineInterpolator}.
*
*/
-
-public class BlackbodySpectrum {
-
- private double reductionFactor;
- private UnivariateFunction interpolation;
-
- /**
- * Creates a {@code BlackbodySpectrum}. Calculates the reduction factor
- * δTm/T0, which
- * is needed for calculations of the maximum heating. Note the interpolation
- * needs to be set
- * @param p a problem statement
- */
-
- public BlackbodySpectrum(NonlinearProblem p) {
- final double maxHeating = p.getProperties().maximumHeating((Pulse2D)p.getPulse());
- reductionFactor = maxHeating / ((double) p.getProperties().getTestTemperature().getValue());
- }
-
- /**
- * Calculates the spectral radiance, which is equal to the spectral power
- * divided by π, at the given coordinate.
- *
- * @param x the geometric coordinate at which calculation should be performed
- * @return the spectral radiance at {@code x}
- */
-
- public double radianceAt(double x) {
- return radiance(interpolation.value(x));
- }
-
- /**
- * Calculates the emissive power at the given coordinate. This is equal to
- * 0.25 T0/δTm [1
- * +δTm /T0 θ (x)
- * ]4, where θ is the reduced temperature.
- *
- * @param x the geometric coordinate inside the sample
- * @return the local emissive power value
- */
-
- public double powerAt(double x) {
- return emissivePower(interpolation.value(x));
- }
-
- /**
- * Sets a new function for the spatial temperature profile. The function is
- * generally constructed using a {@code SplineInterpolator}
- *
- * @param interpolation
- */
-
- public void setInterpolation(UnivariateFunction interpolation) {
- this.interpolation = interpolation;
- }
-
- 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) {
- return emissivePower(reducedTemperature) / Math.PI;
- }
+public class BlackbodySpectrum implements Serializable {
+
+ private static final long serialVersionUID = 4628793608666198231L;
+ private transient UnivariateFunction interpolation;
+ private double reductionFactor;
+
+ /**
+ * Creates a {@code BlackbodySpectrum}. Calculates the reduction factor
+ * δTm/T0, which is
+ * needed for calculations of the maximum heating. Note the interpolation
+ * needs to be set
+ *
+ * @param p a problem statement
+ */
+ public BlackbodySpectrum(NonlinearProblem p) {
+ final double maxHeating = p.getProperties().maximumHeating((Pulse2D) p.getPulse());
+ 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 T0/δTm [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.
+ *
+ * @param x the geometric coordinate at which calculation should be
+ * performed
+ * @return the spectral radiance at {@code x}
+ */
+ public double radianceAt(double x) {
+ return radiance(interpolation.value(x));
+ }
+
+ /**
+ * Calculates the emissive power at the given coordinate.
+ *
+ * @param x the geometric coordinate inside the sample
+ * @return the local emissive power value
+ */
+ public double powerAt(double x) {
+ return emissivePower(interpolation.value(x));
+ }
+
+ /**
+ * Sets a new function for the spatial temperature profile. The function is
+ * generally constructed using a {@code SplineInterpolator}
+ *
+ * @param interpolation
+ */
+ public void setInterpolation(UnivariateFunction interpolation) {
+ this.interpolation = interpolation;
+ }
+
+ public UnivariateFunction getInterpolation() {
+ return interpolation;
+ }
+
+ public final double radiance(double reducedTemperature) {
+ return emissivePower(reducedTemperature) / Math.PI;
+ }
+
+ /*
+ * Serialization
+ */
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException {
+ // default serialization
+ oos.defaultWriteObject();
+ // write the object
+ FunctionSerializer.writeSplineFunction((PolynomialSplineFunction) interpolation, oos);
+ }
+
+ private void readObject(ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ // default deserialization
+ ois.defaultReadObject();
+ this.interpolation = FunctionSerializer.readSplineFunction(ois);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/pulse/problem/schemes/rte/DerivativeCalculator.java b/src/main/java/pulse/problem/schemes/rte/DerivativeCalculator.java
index 69b32644..23a128fa 100644
--- a/src/main/java/pulse/problem/schemes/rte/DerivativeCalculator.java
+++ b/src/main/java/pulse/problem/schemes/rte/DerivativeCalculator.java
@@ -1,63 +1,61 @@
package pulse.problem.schemes.rte;
+import java.io.Serializable;
+
/**
- * This is basically a coupling interface between a {@code Solver} and a {@code RadiativeTransferSolver}.
+ * This is basically a coupling interface between a {@code Solver} and a
+ * {@code RadiativeTransferSolver}.
*
*/
-
-public interface DerivativeCalculator {
-
- /**
- * Calculates the average value of the flux derivatives at the {@code uIndex}
- * grid point on the current and previous timesteps.
- *
- * @param uIndex the grid point index
- * @return the time-averaged value of the flux derivative at {@code uIndex}
- */
-
- public double meanFluxDerivative(int uIndex);
-
- /**
- * Calculates the average value of the flux derivatives at the first grid point
- * on the current and previous timesteps.
- *
- * @return the time-averaged value of the flux derivative at the front surface
- */
-
- public double meanFluxDerivativeFront();
-
- /**
- * Calculates the average value of the flux derivatives at the last grid point
- * on the current and previous timesteps.
- *
- * @return the time-averaged value of the flux derivative at the rear surface
- */
-
- public double meanFluxDerivativeRear();
-
- /**
- * Calculates the flux derivative at the {@code uIndex} grid point.
- *
- * @param uIndex the grid point index
- * @return the value of the flux derivative at {@code uIndex}
- */
-
- public double fluxDerivative(int uIndex);
-
- /**
- * Calculates the flux derivative at the front surface.
- *
- * @return the value of the flux derivative at the front surface
- */
-
- public double fluxDerivativeFront();
-
- /**
- * Calculates the flux derivative at the rear surface.
- *
- * @return the value of the flux derivative at the rear surface
- */
-
- public double fluxDerivativeRear();
-
-}
\ No newline at end of file
+public interface DerivativeCalculator extends Serializable {
+
+ /**
+ * Calculates the average value of the flux derivatives at the
+ * {@code uIndex} grid point on the current and previous timesteps.
+ *
+ * @param uIndex the grid point index
+ * @return the time-averaged value of the flux derivative at {@code uIndex}
+ */
+ public double meanFluxDerivative(int uIndex);
+
+ /**
+ * Calculates the average value of the flux derivatives at the first grid
+ * point on the current and previous timesteps.
+ *
+ * @return the time-averaged value of the flux derivative at the front
+ * surface
+ */
+ public double meanFluxDerivativeFront();
+
+ /**
+ * Calculates the average value of the flux derivatives at the last grid
+ * point on the current and previous timesteps.
+ *
+ * @return the time-averaged value of the flux derivative at the rear
+ * surface
+ */
+ public double meanFluxDerivativeRear();
+
+ /**
+ * Calculates the flux derivative at the {@code uIndex} grid point.
+ *
+ * @param uIndex the grid point index
+ * @return the value of the flux derivative at {@code uIndex}
+ */
+ public double fluxDerivative(int uIndex);
+
+ /**
+ * Calculates the flux derivative at the front surface.
+ *
+ * @return the value of the flux derivative at the front surface
+ */
+ public double fluxDerivativeFront();
+
+ /**
+ * Calculates the flux derivative at the rear surface.
+ *
+ * @return the value of the flux derivative at the rear surface
+ */
+ public double fluxDerivativeRear();
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/Fluxes.java b/src/main/java/pulse/problem/schemes/rte/Fluxes.java
index a086a2b2..be74c53d 100644
--- a/src/main/java/pulse/problem/schemes/rte/Fluxes.java
+++ b/src/main/java/pulse/problem/schemes/rte/Fluxes.java
@@ -1,80 +1,94 @@
package pulse.problem.schemes.rte;
+import java.util.Arrays;
+import static pulse.problem.schemes.rte.RTECalculationStatus.INVALID_FLUXES;
+import static pulse.problem.schemes.rte.RTECalculationStatus.NORMAL;
import pulse.properties.NumericProperty;
public abstract class Fluxes implements DerivativeCalculator {
- private int N;
- private double opticalThickness;
- private double[] fluxes;
- private double[] storedFluxes;
-
- public Fluxes(NumericProperty gridDensity, NumericProperty opticalThickness) {
- setOpticalThickness(opticalThickness);
- setDensity(gridDensity);
- }
-
- /**
- * Stores all currently calculated fluxes in a separate array.
- */
-
- public void store() {
- System.arraycopy(fluxes, 0, storedFluxes, 0, N + 1); // store previous results
- }
-
- /**
- * Retrieves the currently calculated flux at the {@code i} grid point
- *
- * @param i the index of the grid point
- * @return the flux value at the specified grid point
- */
-
- public double getFlux(int i) {
- return fluxes[i];
- }
-
- /**
- * Sets the flux at the {@code i} grid point
- *
- * @param i the index of the grid point
- */
-
- public void setFlux(int i, double value) {
- this.fluxes[i] = value;
- }
-
- /**
- * Retrieves the previously calculated flux at the {@code i} grid point.
- *
- * @param i the index of the grid point
- * @return the previous flux value at the specified grid point
- * @see store()
- */
-
- public double getStoredFlux(int i) {
- return storedFluxes[i];
- }
-
- public double getOpticalGridStep() {
- return opticalThickness/((double)N);
- }
-
- public int getDensity() {
- return N;
- }
-
- public double getOpticalThickness() {
- return opticalThickness;
- }
-
- public void setDensity(NumericProperty gridDensity) {
- this.N = (int)gridDensity.getValue();
- fluxes = new double[N + 1];
- storedFluxes = new double[N + 1];
- }
-
- public void setOpticalThickness(NumericProperty opticalThickness) {
- this.opticalThickness = (double)opticalThickness.getValue();
- }
-
-}
\ No newline at end of file
+ private int N;
+ private double opticalThickness;
+ private double[] fluxes;
+ private double[] storedFluxes;
+
+ public Fluxes(NumericProperty gridDensity, NumericProperty opticalThickness) {
+ setOpticalThickness(opticalThickness);
+ setDensity(gridDensity);
+ }
+
+ /**
+ * Stores all currently calculated fluxes in a separate array.
+ */
+ public void store() {
+ System.arraycopy(fluxes, 0, storedFluxes, 0, N + 1); // store previous results
+ }
+
+ /**
+ * Checks whether all stored values are finite. This is equivalent to
+ * summing all elements and checking whether the sum if finite.
+ *
+ * @return {@code true} if the elements are finite.
+ */
+ public RTECalculationStatus checkArrays() {
+ double sum = Arrays.stream(fluxes).sum() + Arrays.stream(storedFluxes).sum();
+ return Double.isFinite(sum) ? NORMAL : INVALID_FLUXES;
+ }
+
+ /**
+ * Retrieves the currently calculated flux at the {@code i} grid point
+ *
+ * @param i the index of the grid point
+ * @return the flux value at the specified grid point
+ */
+ public double getFlux(int i) {
+ return fluxes[i];
+ }
+
+ /**
+ * Sets the flux at the {@code i} grid point
+ *
+ * @param i the index of the grid point
+ */
+ public void setFlux(int i, double value) {
+ this.fluxes[i] = value;
+ }
+
+ /**
+ * Retrieves the previously calculated flux at the {@code i} grid point.
+ *
+ * @param i the index of the grid point
+ * @return the previous flux value at the specified grid point
+ * @see store()
+ */
+ public double getStoredFlux(int i) {
+ return storedFluxes[i];
+ }
+
+ public double getOpticalGridStep() {
+ return opticalThickness / ((double) N);
+ }
+
+ public int getDensity() {
+ return N;
+ }
+
+ public double getOpticalThickness() {
+ return opticalThickness;
+ }
+
+ public final void setDensity(NumericProperty gridDensity) {
+ this.N = (int) gridDensity.getValue();
+ init();
+ }
+
+ public void init() {
+ fluxes = new double[N + 1];
+ storedFluxes = new double[N + 1];
+ }
+
+ public final void setOpticalThickness(NumericProperty opticalThickness) {
+ this.opticalThickness = (double) opticalThickness.getValue();
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/FluxesAndExplicitDerivatives.java b/src/main/java/pulse/problem/schemes/rte/FluxesAndExplicitDerivatives.java
index 9351579a..f569717d 100644
--- a/src/main/java/pulse/problem/schemes/rte/FluxesAndExplicitDerivatives.java
+++ b/src/main/java/pulse/problem/schemes/rte/FluxesAndExplicitDerivatives.java
@@ -4,66 +4,67 @@
public class FluxesAndExplicitDerivatives extends Fluxes {
- private double fd[];
- private double fdStored[];
-
- public FluxesAndExplicitDerivatives(NumericProperty gridDensity, NumericProperty opticalThickness) {
- super(gridDensity, opticalThickness);
- }
-
- @Override
- public void setDensity(NumericProperty gridDensity) {
- super.setDensity(gridDensity);
- fd = new double[getDensity() + 1];
- fdStored = new double[getDensity() + 1];
- }
-
- @Override
- public double fluxDerivative(int index) {
- return fd[index];
- }
-
- @Override
- public double fluxDerivativeRear() {
- return fd[getDensity()];
- }
-
- @Override
- public double fluxDerivativeFront() {
- return fd[0];
- }
-
- @Override
- public void store() {
- super.store();
- System.arraycopy(fd, 0, fdStored, 0, fd.length); // store previous results
- }
-
- @Override
- public double meanFluxDerivative(int uIndex) {
- return 0.5 * (fd[uIndex] + fdStored[uIndex]);
- }
-
- @Override
- public double meanFluxDerivativeFront() {
- return 0.5 * (fd[0] + fdStored[0]);
- }
-
- @Override
- public double meanFluxDerivativeRear() {
- return 0.5 * (fd[getDensity()] + fdStored[getDensity()]);
- }
-
- public double getStoredFluxDerivative(int index) {
- return fdStored[index];
- }
-
- public double getFluxDerivative(int i) {
- return fd[i];
- }
-
- public void setFluxDerivative(int i, double f) {
- fd[i] = f;
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = -6308711091434946173L;
+ private double fd[];
+ private double fdStored[];
+
+ public FluxesAndExplicitDerivatives(NumericProperty gridDensity, NumericProperty opticalThickness) {
+ super(gridDensity, opticalThickness);
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ fd = new double[getDensity() + 1];
+ fdStored = new double[getDensity() + 1];
+ }
+
+ @Override
+ public double fluxDerivative(int index) {
+ return fd[index];
+ }
+
+ @Override
+ public double fluxDerivativeRear() {
+ return fd[getDensity()];
+ }
+
+ @Override
+ public double fluxDerivativeFront() {
+ return fd[0];
+ }
+
+ @Override
+ public void store() {
+ super.store();
+ System.arraycopy(fd, 0, fdStored, 0, fd.length); // store previous results
+ }
+
+ @Override
+ public double meanFluxDerivative(int uIndex) {
+ return 0.5 * (fd[uIndex] + fdStored[uIndex]);
+ }
+
+ @Override
+ public double meanFluxDerivativeFront() {
+ return 0.5 * (fd[0] + fdStored[0]);
+ }
+
+ @Override
+ public double meanFluxDerivativeRear() {
+ return 0.5 * (fd[getDensity()] + fdStored[getDensity()]);
+ }
+
+ public double getStoredFluxDerivative(int index) {
+ return fdStored[index];
+ }
+
+ public double getFluxDerivative(int i) {
+ return fd[i];
+ }
+
+ public void setFluxDerivative(int i, double f) {
+ fd[i] = f;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/FluxesAndImplicitDerivatives.java b/src/main/java/pulse/problem/schemes/rte/FluxesAndImplicitDerivatives.java
index dc9e6f05..038bc2e2 100644
--- a/src/main/java/pulse/problem/schemes/rte/FluxesAndImplicitDerivatives.java
+++ b/src/main/java/pulse/problem/schemes/rte/FluxesAndImplicitDerivatives.java
@@ -3,45 +3,47 @@
import pulse.properties.NumericProperty;
public class FluxesAndImplicitDerivatives extends Fluxes {
-
- public FluxesAndImplicitDerivatives(NumericProperty gridDensity, NumericProperty opticalThickness) {
- super(gridDensity, opticalThickness);
- }
-
- @Override
- public double meanFluxDerivative(int uIndex) {
- double f = (getFlux(uIndex - 1) - getFlux(uIndex + 1))
- + (getStoredFlux(uIndex - 1) - getStoredFlux(uIndex + 1));
- return f * 0.25 / getOpticalGridStep();
- }
-
- @Override
- public double meanFluxDerivativeFront() {
- double f = (getFlux(0) - getFlux(1)) + (getStoredFlux(0) - getStoredFlux(1));
- return f * 0.5 / getOpticalGridStep();
- }
-
- @Override
- public double meanFluxDerivativeRear() {
- final int N = this.getDensity();
- double f = (getFlux(N - 1) - getFlux(N)) + (getStoredFlux(N - 1) - getStoredFlux(N));
- return f * 0.5 / getOpticalGridStep();
- }
-
- @Override
- public double fluxDerivative(int uIndex) {
- return (getFlux(uIndex - 1) - getFlux(uIndex + 1)) * 0.5 / getOpticalGridStep();
- }
-
- @Override
- public double fluxDerivativeFront() {
- return (getFlux(0) - getFlux(1)) / getOpticalGridStep();
- }
-
- @Override
- public double fluxDerivativeRear() {
- final int N = this.getDensity();
- return (getFlux(N - 1) - getFlux(N)) / getOpticalGridStep();
- }
-
-}
\ No newline at end of file
+
+ private static final long serialVersionUID = -4161296401342482405L;
+
+ public FluxesAndImplicitDerivatives(NumericProperty gridDensity, NumericProperty opticalThickness) {
+ super(gridDensity, opticalThickness);
+ }
+
+ @Override
+ public double meanFluxDerivative(int uIndex) {
+ double f = (getFlux(uIndex - 1) - getFlux(uIndex + 1))
+ + (getStoredFlux(uIndex - 1) - getStoredFlux(uIndex + 1));
+ return f * 0.25 / getOpticalGridStep();
+ }
+
+ @Override
+ public double meanFluxDerivativeFront() {
+ double f = (getFlux(0) - getFlux(1)) + (getStoredFlux(0) - getStoredFlux(1));
+ return f * 0.5 / getOpticalGridStep();
+ }
+
+ @Override
+ public double meanFluxDerivativeRear() {
+ final int N = this.getDensity();
+ double f = (getFlux(N - 1) - getFlux(N)) + (getStoredFlux(N - 1) - getStoredFlux(N));
+ return f * 0.5 / getOpticalGridStep();
+ }
+
+ @Override
+ public double fluxDerivative(int uIndex) {
+ return (getFlux(uIndex - 1) - getFlux(uIndex + 1)) * 0.5 / getOpticalGridStep();
+ }
+
+ @Override
+ public double fluxDerivativeFront() {
+ return (getFlux(0) - getFlux(1)) / getOpticalGridStep();
+ }
+
+ @Override
+ public double fluxDerivativeRear() {
+ final int N = this.getDensity();
+ return (getFlux(N - 1) - getFlux(N)) / getOpticalGridStep();
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/RTECalculationListener.java b/src/main/java/pulse/problem/schemes/rte/RTECalculationListener.java
index 607b5334..f761f7c4 100644
--- a/src/main/java/pulse/problem/schemes/rte/RTECalculationListener.java
+++ b/src/main/java/pulse/problem/schemes/rte/RTECalculationListener.java
@@ -1,19 +1,19 @@
package pulse.problem.schemes.rte;
+import java.io.Serializable;
+
/**
* Used to listed to status updates in {@code RadiativeTransferSolver}
* subclasses.
*
*/
+public interface RTECalculationListener extends Serializable {
-public interface RTECalculationListener {
-
- /**
- * Invoked when a sub-step of the RTE solution has finished.
- *
- * @param status the status of the completed step
- */
-
- public void onStatusUpdate(RTECalculationStatus status);
+ /**
+ * Invoked when a sub-step of the RTE solution has finished.
+ *
+ * @param status the status of the completed step
+ */
+ public void onStatusUpdate(RTECalculationStatus status);
-}
\ No newline at end of file
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/RTECalculationStatus.java b/src/main/java/pulse/problem/schemes/rte/RTECalculationStatus.java
index b656dcb4..83054ca1 100644
--- a/src/main/java/pulse/problem/schemes/rte/RTECalculationStatus.java
+++ b/src/main/java/pulse/problem/schemes/rte/RTECalculationStatus.java
@@ -4,30 +4,27 @@
* A measure of health for radiative transfer calculations.
*
*/
-
public enum RTECalculationStatus {
- /**
- * The current calculation step finished normally.
- */
-
- NORMAL,
-
- /**
- * The integrator took too long to finish.
- */
-
- INTEGRATOR_TIMEOUT,
-
- /**
- * The iterative solver took too long to finish.
- */
-
- ITERATION_LIMIT_REACHED,
-
- /**
- * The grid density required to reach the error threshold was too large.
- */
-
- GRID_TOO_LARGE;
-}
\ No newline at end of file
+ /**
+ * The current calculation step finished normally.
+ */
+ NORMAL,
+ /**
+ * The integrator took too long to finish.
+ */
+ INTEGRATOR_TIMEOUT,
+ /**
+ * The iterative solver took too long to finish.
+ */
+ ITERATION_LIMIT_REACHED,
+ /**
+ * The grid density required to reach the error threshold was too large.
+ */
+ GRID_TOO_LARGE,
+ /**
+ * The radiative fluxes contain illegal values.
+ */
+ INVALID_FLUXES;
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/RadiativeTransferSolver.java b/src/main/java/pulse/problem/schemes/rte/RadiativeTransferSolver.java
index 3ae192c0..8013c33c 100644
--- a/src/main/java/pulse/problem/schemes/rte/RadiativeTransferSolver.java
+++ b/src/main/java/pulse/problem/schemes/rte/RadiativeTransferSolver.java
@@ -11,7 +11,7 @@
import pulse.problem.schemes.Grid;
import pulse.problem.statements.ParticipatingMedium;
-import pulse.problem.statements.ThermoOpticalProperties;
+import pulse.problem.statements.model.ThermoOpticalProperties;
import pulse.util.Descriptive;
import pulse.util.PropertyHolder;
import pulse.util.Reflexive;
@@ -24,118 +24,116 @@
* steps with listeners.
*
*/
-
public abstract class RadiativeTransferSolver extends PropertyHolder implements Reflexive, Descriptive {
- private Fluxes fluxes;
- private List rteListeners;
-
- /**
- * Dummy constructor.
- *
- */
-
- public RadiativeTransferSolver() {
- rteListeners = new ArrayList<>();
- }
-
- /**
- * Launches a calculation of the radiative transfer equation.
- *
- * @param temperatureArray the input temperature profile
- * @return the status of calculation
- */
-
- public abstract RTECalculationStatus compute(double[] temperatureArray);
-
- /**
- * Retrieves the parameters from {@code p} and {@code grid} needed to run the
- * calculations. Resets the flux arrays.
- *
- * @param p the problem statement
- * @param grid the grid
- */
-
- public void init(ParticipatingMedium p, Grid grid) {
- if (fluxes != null) {
- fluxes.setDensity(grid.getGridDensity());
- var properties = (ThermoOpticalProperties)p.getProperties();
- fluxes.setOpticalThickness(properties.getOpticalThickness());
- }
- }
-
- /**
- * Performs interpolation with natural cubic splines using the input arguments.
- *
- * @param tempArray an array of data defined on a previously initialised grid.
- * @return a {@code UnivariateFunction} generated with a
- * {@code SplineInterpolator}
- */
-
- public UnivariateFunction interpolateTemperatureProfile(final double[] tempArray) {
- var xArray = new double[tempArray.length + 2];
- IntStream.range(0, xArray.length).forEach(i -> xArray[i] = opticalCoordinateAt(i - 1));
-
- var tarray = new double[tempArray.length + 2];
- System.arraycopy(tempArray, 0, tarray, 1, tempArray.length - 1);
-
- final double[] p1 = new double[] { xArray[1], tempArray[0] };
- final double[] p2 = new double[] { xArray[2], tempArray[1] };
- tarray[0] = linearExtrapolation(p1, p2, xArray[0]);
-
- final double[] p3 = new double[] { xArray[xArray.length - 2], tempArray[tempArray.length - 1] };
- final double[] p4 = new double[] { xArray[xArray.length - 3], tempArray[tempArray.length - 2] };
- tarray[tarray.length - 1] = linearExtrapolation(p3, p4, xArray[xArray.length - 1]);
-
- return (new SplineInterpolator()).interpolate(xArray, tarray);
- }
-
- /**
- * Retrieves the optical coordinate corresponding to the grid index {@code i}
- *
- * @param i the external grid index
- * @return τ0/N i
- */
-
- public double opticalCoordinateAt(final int i) {
- return fluxes.getOpticalGridStep() * i;
- }
-
- @Override
- public boolean ignoreSiblings() {
- return true;
- }
-
- @Override
- public String getPrefix() {
- return "Radiative Transfer Solver";
- }
-
- public List getRTEListeners() {
- return rteListeners;
- }
-
- /**
- * Adds a listener that can listen to status updates.
- *
- * @param listener a listener to track the calculation progress
- */
-
- public void addRTEListener(RTECalculationListener listener) {
- rteListeners.add(listener);
- }
-
- public void fireStatusUpdate(RTECalculationStatus status) {
- for (RTECalculationListener l : getRTEListeners())
- l.onStatusUpdate(status);
- }
-
- public Fluxes getFluxes() {
- return fluxes;
- }
-
- public void setFluxes(Fluxes fluxes) {
- this.fluxes = fluxes;
- }
-
-}
\ No newline at end of file
+ private Fluxes fluxes;
+ private final List rteListeners;
+
+ /**
+ * Dummy constructor.
+ *
+ */
+ public RadiativeTransferSolver() {
+ rteListeners = new ArrayList<>();
+ }
+
+ /**
+ * Launches a calculation of the radiative transfer equation.
+ *
+ * @param temperatureArray the input temperature profile
+ * @return the status of calculation
+ */
+ public abstract RTECalculationStatus compute(double[] temperatureArray);
+
+ /**
+ * Retrieves the parameters from {@code p} and {@code grid} needed to run
+ * the calculations.Resets the flux arrays.
+ *
+ * @param p
+ * @param grid the grid
+ */
+ public void init(ParticipatingMedium p, Grid grid) {
+ if (fluxes != null) {
+ fluxes.setDensity(grid.getGridDensity());
+ fluxes.init();
+ ThermoOpticalProperties top = (ThermoOpticalProperties) p.getProperties();
+ fluxes.setOpticalThickness(top.getOpticalThickness());
+ }
+ }
+
+ /**
+ * Performs interpolation with natural cubic splines using the input
+ * arguments.
+ *
+ * @param tempArray an array of data defined on a previously initialised
+ * grid.
+ * @return a {@code UnivariateFunction} generated with a
+ * {@code SplineInterpolator}
+ */
+ public UnivariateFunction interpolateTemperatureProfile(final double[] tempArray) {
+ var xArray = new double[tempArray.length + 2];
+ IntStream.range(0, xArray.length).forEach(i -> xArray[i] = opticalCoordinateAt(i - 1));
+
+ var tarray = new double[tempArray.length + 2];
+ System.arraycopy(tempArray, 0, tarray, 1, tempArray.length - 1);
+
+ final double[] p1 = new double[]{xArray[1], tempArray[0]};
+ final double[] p2 = new double[]{xArray[2], tempArray[1]};
+ tarray[0] = linearExtrapolation(p1, p2, xArray[0]);
+
+ final double[] p3 = new double[]{xArray[xArray.length - 2], tempArray[tempArray.length - 1]};
+ final double[] p4 = new double[]{xArray[xArray.length - 3], tempArray[tempArray.length - 2]};
+ tarray[tarray.length - 1] = linearExtrapolation(p3, p4, xArray[xArray.length - 1]);
+
+ return (new SplineInterpolator()).interpolate(xArray, tarray);
+ }
+
+ /**
+ * Retrieves the optical coordinate corresponding to the grid index
+ * {@code i}
+ *
+ * @param i the external grid index
+ * @return τ0/N i
+ */
+ public double opticalCoordinateAt(final int i) {
+ return fluxes.getOpticalGridStep() * i;
+ }
+
+ @Override
+ public boolean ignoreSiblings() {
+ return true;
+ }
+
+ @Override
+ public String getPrefix() {
+ return "Radiative Transfer Solver";
+ }
+
+ public List getRTEListeners() {
+ return rteListeners;
+ }
+
+ /**
+ * Adds a listener that can listen to status updates.
+ *
+ * @param listener a listener to track the calculation progress
+ */
+ public void addRTEListener(RTECalculationListener listener) {
+ rteListeners.add(listener);
+ }
+
+ public void fireStatusUpdate(RTECalculationStatus status) {
+ for (RTECalculationListener l : getRTEListeners()) {
+ l.onStatusUpdate(status);
+ }
+ }
+
+ public final Fluxes getFluxes() {
+ return fluxes;
+ }
+
+ public final void setFluxes(Fluxes fluxes) {
+ this.fluxes = fluxes;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/AdaptiveIntegrator.java b/src/main/java/pulse/problem/schemes/rte/dom/AdaptiveIntegrator.java
index 614f1bd1..efbba180 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/AdaptiveIntegrator.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/AdaptiveIntegrator.java
@@ -11,253 +11,253 @@
import java.time.Duration;
import java.time.Instant;
-import java.util.List;
+import java.util.Set;
import pulse.math.linear.Vector;
import pulse.problem.schemes.rte.RTECalculationStatus;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
-import pulse.properties.Property;
/**
* An ODE integrator with an adaptive step size.
*
*/
-
public abstract class AdaptiveIntegrator extends ODEIntegrator {
- private HermiteInterpolator hermite;
-
- private double atol;
- private double rtol;
- private double scalingFactor;
-
- private boolean firstRun;
- private boolean rescaled;
-
- private Instant start;
- private double timeThreshold;
-
- public AdaptiveIntegrator(Discretisation intensities) {
- super(intensities);
- atol = (double) def(ATOL).getValue();
- rtol = (double) def(RTOL).getValue();
- scalingFactor = (double) def(GRID_SCALING_FACTOR).getValue();
- timeThreshold = (double) def(RTE_INTEGRATION_TIMEOUT).getValue();
- hermite = new HermiteInterpolator();
- }
-
- @Override
- public RTECalculationStatus integrate() {
- Vector[] v;
- final var intensities = getDiscretisation();
- final var quantities = intensities.getQuantities();
-
- int N = intensities.getGrid().getDensity();
- final int total = intensities.getOrdinates().getTotalNodes();
- rescaled = false;
-
- final int nPositiveStart = intensities.getOrdinates().getFirstPositiveNode();
- final int nNegativeStart = intensities.getOrdinates().getFirstNegativeNode();
- final int halfLength = nNegativeStart - nPositiveStart;
-
- RTECalculationStatus status = RTECalculationStatus.NORMAL;
-
- for (double error = 1.0, relFactor = 0.0, i0Max = 0, i1Max = 0; (error > atol + relFactor * rtol)
- && status == RTECalculationStatus.NORMAL; N = intensities.getGrid()
- .getDensity(), status = sanityCheck()) {
-
- start = Instant.now();
- error = 0;
-
- treatZeroIndex();
-
- /*
+ private HermiteInterpolator hermite;
+
+ private double atol;
+ private double rtol;
+ private double scalingFactor;
+
+ private boolean firstRun;
+ private boolean rescaled;
+
+ private Instant start;
+ private double timeThreshold;
+
+ public AdaptiveIntegrator(Discretisation intensities) {
+ super(intensities);
+ atol = (double) def(ATOL).getValue();
+ rtol = (double) def(RTOL).getValue();
+ scalingFactor = (double) def(GRID_SCALING_FACTOR).getValue();
+ timeThreshold = (double) def(RTE_INTEGRATION_TIMEOUT).getValue();
+ hermite = new HermiteInterpolator();
+ }
+
+ @Override
+ public RTECalculationStatus integrate() {
+ Vector[] v;
+ final var intensities = getDiscretisation();
+ final var quantities = intensities.getQuantities();
+
+ int N = intensities.getGrid().getDensity();
+ final int total = intensities.getOrdinates().getTotalNodes();
+ rescaled = false;
+
+ final int nPositiveStart = intensities.getOrdinates().getFirstPositiveNode();
+ final int nNegativeStart = intensities.getOrdinates().getFirstNegativeNode();
+ final int halfLength = nNegativeStart - nPositiveStart;
+
+ RTECalculationStatus status = RTECalculationStatus.NORMAL;
+
+ for (double error = 1.0, relFactor = 0.0, i0Max = 0, i1Max = 0; (error > atol + relFactor * rtol)
+ && status == RTECalculationStatus.NORMAL; N = intensities.getGrid()
+ .getDensity(), status = sanityCheck()) {
+
+ start = Instant.now();
+ error = 0;
+
+ treatZeroIndex();
+
+ /*
* First set of ODE's. Initial condition corresponds to I(0) /t ----> tau0 The
* streams propagate in the positive hemisphere
- */
-
- intensities.intensitiesLeftBoundary(getEmissionFunction()); // initial value for tau = 0
- i0Max = (new Vector(quantities.getIntensities()[0])).maxAbsComponent();
+ */
+ intensities.intensitiesLeftBoundary(getEmissionFunction()); // initial value for tau = 0
+ i0Max = (new Vector(quantities.getIntensities()[0])).maxAbsComponent();
- firstRun = true;
+ firstRun = true;
- for (int j = 0; j < N && error < atol + relFactor * rtol; j++) {
+ for (int j = 0; j < N && error < atol + relFactor * rtol; j++) {
- v = step(j, 1.0);
- System.arraycopy(v[0].getData(), 0, quantities.getIntensities()[j + 1], nPositiveStart, halfLength);
+ v = step(j, 1.0);
+ System.arraycopy(v[0].getData(), 0, quantities.getIntensities()[j + 1], nPositiveStart, halfLength);
- i1Max = (new Vector(quantities.getIntensities()[j + 1])).maxAbsComponent();
- relFactor = Math.max(i0Max, i1Max);
- i0Max = i1Max;
+ i1Max = (new Vector(quantities.getIntensities()[j + 1])).maxAbsComponent();
+ relFactor = Math.max(i0Max, i1Max);
+ i0Max = i1Max;
- error = v[1].maxAbsComponent();
- }
+ error = v[1].maxAbsComponent();
+ }
- /*
+ /*
* Second set of ODE. Initial condition corresponds to I(tau0) /0 <---- t The
* streams propagate in the negative hemisphere
- */
-
- intensities.intensitiesRightBoundary(getEmissionFunction()); // initial value for tau = tau_0
- i0Max = (new Vector(quantities.getIntensities()[N])).maxAbsComponent();
-
- firstRun = true;
-
- for (int j = N; j > 0 && error < atol + relFactor * rtol; j--) {
-
- v = step(j, -1.0);
- System.arraycopy(v[0].getData(), 0, quantities.getIntensities()[j - 1], nNegativeStart, halfLength);
-
- i1Max = (new Vector(quantities.getIntensities()[j - 1])).maxAbsComponent();
- relFactor = Math.max(i0Max, i1Max);
- i0Max = i1Max;
-
- error = v[1].maxAbsComponent();
- }
-
- // store derivatives for Hermite interpolation
- for (int i = 0; i < total; i++) {
- quantities.setDerivative(N, i, quantities.getDerivative(N - 1, i));
- quantities.setDerivative(0, i, quantities.getDerivative(1, i));
- }
-
- if (error > atol + relFactor * rtol) {
- reduceStepSize();
- hermite.clear();
- }
-
- }
-
- return status;
-
- }
-
- private RTECalculationStatus sanityCheck() {
- if (!isValueSensible(def(DOM_GRID_DENSITY),
- getDiscretisation().getGrid().getDensity()))
- return RTECalculationStatus.GRID_TOO_LARGE;
- else if (Duration.between(Instant.now(), start).toSeconds() > timeThreshold)
- return RTECalculationStatus.INTEGRATOR_TIMEOUT;
- return RTECalculationStatus.NORMAL;
- }
-
- public abstract Vector[] step(final int j, final double sign);
-
- public void reduceStepSize() {
- var intensities = getDiscretisation();
- final int nNew = (roundEven(scalingFactor * intensities.getGrid().getDensity()));
- generateGrid(nNew);
- intensities.getQuantities().init(intensities.getGrid().getDensity(), intensities.getOrdinates().getTotalNodes());
- rescaled = true;
- }
-
- public boolean wasRescaled() {
- return rescaled;
- }
-
- /**
- * Generates a uniform grid using the argument as the density.
- *
- * @param nNew new grid density
- */
-
- public void generateGrid(int nNew) {
- getDiscretisation().getGrid().generateUniformBase(nNew, true);
- }
-
- private int roundEven(double a) {
- return (int) (a / 2 * 2);
- }
-
- public NumericProperty getRelativeTolerance() {
- return derive(RTOL, rtol);
- }
-
- public NumericProperty getAbsoluteTolerance() {
- return derive(ATOL, atol);
- }
-
- public NumericProperty getGridScalingFactor() {
- return derive(GRID_SCALING_FACTOR, scalingFactor);
- }
-
- public void setRelativeTolerance(NumericProperty p) {
- if (p.getType() != RTOL)
- throw new IllegalArgumentException("Illegal type: " + p.getType());
- this.rtol = (double) p.getValue();
- }
-
- public void setAbsoluteTolerance(NumericProperty p) {
- if (p.getType() != ATOL)
- throw new IllegalArgumentException("Illegal type: " + p.getType());
- this.atol = (double) p.getValue();
- }
-
- public void setGridScalingFactor(NumericProperty p) {
- if (p.getType() != GRID_SCALING_FACTOR)
- throw new IllegalArgumentException("Illegal type: " + p.getType());
- this.scalingFactor = (double) p.getValue();
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- switch (type) {
- case RTOL:
- setRelativeTolerance(property);
- break;
- case ATOL:
- setAbsoluteTolerance(property);
- break;
- case GRID_SCALING_FACTOR:
- setGridScalingFactor(property);
- break;
- case RTE_INTEGRATION_TIMEOUT:
- setTimeThreshold(property);
- break;
- default:
- return;
- }
-
- firePropertyChanged(this, property);
-
- }
-
- @Override
- public List listedTypes() {
- List list = super.listedTypes();
- list.add(def(RTOL));
- list.add(def(ATOL));
- list.add(def(GRID_SCALING_FACTOR));
- list.add(def(RTE_INTEGRATION_TIMEOUT));
- return list;
- }
-
- @Override
- public String toString() {
- return super.toString() + " : " + this.getRelativeTolerance() + " ; " + this.getAbsoluteTolerance() + " ; "
- + this.getGridScalingFactor();
- }
-
- public NumericProperty getTimeThreshold() {
- return derive(RTE_INTEGRATION_TIMEOUT, (double) timeThreshold);
- }
-
- public void setTimeThreshold(NumericProperty timeThreshold) {
- if (timeThreshold.getType() == RTE_INTEGRATION_TIMEOUT)
- this.timeThreshold = ((Number) timeThreshold.getValue()).longValue();
- }
-
- public boolean isFirstRun() {
- return firstRun;
- }
-
- public void setFirstRun(boolean firstRun) {
- this.firstRun = firstRun;
- }
-
- public HermiteInterpolator getHermiteInterpolator() {
- return hermite;
- }
-
-}
\ No newline at end of file
+ */
+ intensities.intensitiesRightBoundary(getEmissionFunction()); // initial value for tau = tau_0
+ i0Max = (new Vector(quantities.getIntensities()[N])).maxAbsComponent();
+
+ firstRun = true;
+
+ for (int j = N; j > 0 && error < atol + relFactor * rtol; j--) {
+
+ v = step(j, -1.0);
+ System.arraycopy(v[0].getData(), 0, quantities.getIntensities()[j - 1], nNegativeStart, halfLength);
+
+ i1Max = (new Vector(quantities.getIntensities()[j - 1])).maxAbsComponent();
+ relFactor = Math.max(i0Max, i1Max);
+ i0Max = i1Max;
+
+ error = v[1].maxAbsComponent();
+ }
+
+ // store derivatives for Hermite interpolation
+ for (int i = 0; i < total; i++) {
+ quantities.setDerivative(N, i, quantities.getDerivative(N - 1, i));
+ quantities.setDerivative(0, i, quantities.getDerivative(1, i));
+ }
+
+ if (error > atol + relFactor * rtol) {
+ reduceStepSize();
+ hermite.clear();
+ }
+
+ }
+
+ return status;
+
+ }
+
+ private RTECalculationStatus sanityCheck() {
+ if (!isValueSensible(def(DOM_GRID_DENSITY),
+ getDiscretisation().getGrid().getDensity())) {
+ return RTECalculationStatus.GRID_TOO_LARGE;
+ } else if (Duration.between(Instant.now(), start).toSeconds() > timeThreshold) {
+ return RTECalculationStatus.INTEGRATOR_TIMEOUT;
+ }
+ return RTECalculationStatus.NORMAL;
+ }
+
+ public abstract Vector[] step(final int j, final double sign);
+
+ public void reduceStepSize() {
+ var intensities = getDiscretisation();
+ final int nNew = (roundEven(scalingFactor * intensities.getGrid().getDensity()));
+ generateGrid(nNew);
+ intensities.getQuantities().init(intensities.getGrid().getDensity(), intensities.getOrdinates().getTotalNodes());
+ rescaled = true;
+ }
+
+ public boolean wasRescaled() {
+ return rescaled;
+ }
+
+ /**
+ * Generates a uniform grid using the argument as the density.
+ *
+ * @param nNew new grid density
+ */
+ public void generateGrid(int nNew) {
+ getDiscretisation().getGrid().generateUniformBase(nNew, true);
+ }
+
+ private int roundEven(double a) {
+ return (int) (a / 2 * 2);
+ }
+
+ public NumericProperty getRelativeTolerance() {
+ return derive(RTOL, rtol);
+ }
+
+ public NumericProperty getAbsoluteTolerance() {
+ return derive(ATOL, atol);
+ }
+
+ public NumericProperty getGridScalingFactor() {
+ return derive(GRID_SCALING_FACTOR, scalingFactor);
+ }
+
+ public void setRelativeTolerance(NumericProperty p) {
+ if (p.getType() != RTOL) {
+ throw new IllegalArgumentException("Illegal type: " + p.getType());
+ }
+ this.rtol = (double) p.getValue();
+ }
+
+ public void setAbsoluteTolerance(NumericProperty p) {
+ if (p.getType() != ATOL) {
+ throw new IllegalArgumentException("Illegal type: " + p.getType());
+ }
+ this.atol = (double) p.getValue();
+ }
+
+ public void setGridScalingFactor(NumericProperty p) {
+ if (p.getType() != GRID_SCALING_FACTOR) {
+ throw new IllegalArgumentException("Illegal type: " + p.getType());
+ }
+ this.scalingFactor = (double) p.getValue();
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ switch (type) {
+ case RTOL:
+ setRelativeTolerance(property);
+ break;
+ case ATOL:
+ setAbsoluteTolerance(property);
+ break;
+ case GRID_SCALING_FACTOR:
+ setGridScalingFactor(property);
+ break;
+ case RTE_INTEGRATION_TIMEOUT:
+ setTimeThreshold(property);
+ break;
+ default:
+ return;
+ }
+
+ firePropertyChanged(this, property);
+
+ }
+
+ @Override
+ public Set listedKeywords() {
+ var set = super.listedKeywords();
+ set.add(RTOL);
+ set.add(ATOL);
+ set.add(GRID_SCALING_FACTOR);
+ set.add(RTE_INTEGRATION_TIMEOUT);
+ return set;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " : " + this.getRelativeTolerance() + " ; " + this.getAbsoluteTolerance() + " ; "
+ + this.getGridScalingFactor();
+ }
+
+ public NumericProperty getTimeThreshold() {
+ return derive(RTE_INTEGRATION_TIMEOUT, (double) timeThreshold);
+ }
+
+ public void setTimeThreshold(NumericProperty timeThreshold) {
+ if (timeThreshold.getType() == RTE_INTEGRATION_TIMEOUT) {
+ this.timeThreshold = ((Number) timeThreshold.getValue()).longValue();
+ }
+ }
+
+ public boolean isFirstRun() {
+ return firstRun;
+ }
+
+ public void setFirstRun(boolean firstRun) {
+ this.firstRun = firstRun;
+ }
+
+ public HermiteInterpolator getHermiteInterpolator() {
+ return hermite;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/ButcherTableau.java b/src/main/java/pulse/problem/schemes/rte/dom/ButcherTableau.java
index a32c75c8..5d86e8f0 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/ButcherTableau.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/ButcherTableau.java
@@ -1,129 +1,132 @@
package pulse.problem.schemes.rte.dom;
+import java.io.Serializable;
import pulse.math.linear.Matrices;
import pulse.math.linear.SquareMatrix;
import pulse.math.linear.Vector;
import pulse.util.Descriptive;
/**
- * The Butcher tableau coefficients used by the explicit Runge-Kutta solvers. Variable
- * names correspond to the standard notations.
+ * The Butcher tableau coefficients used by the explicit Runge-Kutta solvers.
+ * Variable names correspond to the standard notations.
*
*/
+public class ButcherTableau implements Descriptive, Serializable {
-public class ButcherTableau implements Descriptive {
+ private static final long serialVersionUID = -8856270519744473886L;
+ private Vector b;
+ private Vector bHat;
+ private Vector c;
+ private SquareMatrix coefs;
- private Vector b;
- private Vector bHat;
- private Vector c;
- private SquareMatrix coefs;
+ private boolean fsal;
+ private String name;
- private boolean fsal;
- private String name;
+ public final static String DEFAULT_TABLEAU = "BS23";
- public final static String DEFAULT_TABLEAU = "BS23";
+ public ButcherTableau(String name, double[][] coefs, double[] c, double[] b, double[] bHat, boolean fsal) {
- public ButcherTableau(String name, double[][] coefs, double[] c, double[] b, double[] bHat, boolean fsal) {
+ if (c.length != b.length || c.length != bHat.length) {
+ throw new IllegalArgumentException("Check dimensions of the input vectors");
+ }
- if (c.length != b.length || c.length != bHat.length)
- throw new IllegalArgumentException("Check dimensions of the input vectors");
+ if (coefs.length != coefs[0].length || coefs.length != c.length) {
+ throw new IllegalArgumentException("Check dimensions of the input matrix array");
+ }
- if (coefs.length != coefs[0].length || coefs.length != c.length)
- throw new IllegalArgumentException("Check dimensions of the input matrix array");
+ this.name = name;
+ this.fsal = fsal;
- this.name = name;
- this.fsal = fsal;
+ this.coefs = Matrices.createSquareMatrix(coefs);
+ this.c = new Vector(c);
+ this.b = new Vector(b);
+ this.bHat = new Vector(bHat);
+ }
- this.coefs = Matrices.createMatrix(coefs);
- this.c = new Vector(c);
- this.b = new Vector(b);
- this.bHat = new Vector(bHat);
- }
+ public int numberOfStages() {
+ return b.dimension();
+ }
- public int numberOfStages() {
- return b.dimension();
- }
+ public SquareMatrix getMatrix() {
+ return coefs;
+ }
- public SquareMatrix getMatrix() {
- return coefs;
- }
+ public void setMatrix(SquareMatrix coefs) {
+ this.coefs = coefs;
+ }
- public void setMatrix(SquareMatrix coefs) {
- this.coefs = coefs;
- }
+ public Vector getEstimator() {
+ return bHat;
+ }
- public Vector getEstimator() {
- return bHat;
- }
+ public void setEstimator(Vector bHat) {
+ this.bHat = bHat;
+ }
- public void setEstimator(Vector bHat) {
- this.bHat = bHat;
- }
+ public Vector getInterpolator() {
+ return b;
+ }
- public Vector getInterpolator() {
- return b;
- }
+ public void setInterpolator(Vector b) {
+ this.b = b;
+ }
- public void setInterpolator(Vector b) {
- this.b = b;
- }
+ public Vector getC() {
+ return c;
+ }
- public Vector getC() {
- return c;
- }
+ public void setC(Vector c) {
+ this.c = c;
+ }
- public void setC(Vector c) {
- this.c = c;
- }
+ public boolean isFSAL() {
+ return fsal;
+ }
- public boolean isFSAL() {
- return fsal;
- }
+ @Override
+ public String toString() {
+ return name;
+ }
- @Override
- public String toString() {
- return name;
- }
-
- public String printTableau() {
+ public String printTableau() {
- StringBuilder sb = new StringBuilder();
+ StringBuilder sb = new StringBuilder();
- for (int i = 0; i < b.dimension(); i++) {
+ for (int i = 0; i < b.dimension(); i++) {
- sb.append(String.format("%n%3.8f | ", c.get(i)));
+ sb.append(String.format("%n%3.8f | ", c.get(i)));
- for (int j = 0; j < b.dimension(); j++) {
- sb.append(String.format("%3.8f ", coefs.get(i, j)));
- }
+ for (int j = 0; j < b.dimension(); j++) {
+ sb.append(String.format("%3.8f ", coefs.get(i, j)));
+ }
- }
+ }
- sb.append(System.lineSeparator());
+ sb.append(System.lineSeparator());
- for (int i = 0; i < b.dimension() + 1; i++) {
- sb.append(String.format("%-12s", "-"));
- }
+ for (int i = 0; i < b.dimension() + 1; i++) {
+ sb.append(String.format("%-12s", "-"));
+ }
- sb.append(System.lineSeparator() + String.format("%-10s | ", "-"));
+ sb.append(System.lineSeparator() + String.format("%-10s | ", "-"));
- for (int i = 0; i < b.dimension(); i++) {
- sb.append(String.format("%3.8f ", b.get(i)));
- }
+ for (int i = 0; i < b.dimension(); i++) {
+ sb.append(String.format("%3.8f ", b.get(i)));
+ }
- sb.append(System.lineSeparator() + String.format("%-10s | ", "-"));
+ sb.append(System.lineSeparator() + String.format("%-10s | ", "-"));
- for (int i = 0; i < b.dimension(); i++) {
- sb.append(String.format("%3.8f ", bHat.get(i)));
- }
+ for (int i = 0; i < b.dimension(); i++) {
+ sb.append(String.format("%3.8f ", bHat.get(i)));
+ }
- return sb.toString();
+ return sb.toString();
- }
+ }
- @Override
- public String describe() {
- return "Butcher tableau";
- }
+ @Override
+ public String describe() {
+ return "Butcher tableau";
+ }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/CompositeGaussianQuadrature.java b/src/main/java/pulse/problem/schemes/rte/dom/CompositeGaussianQuadrature.java
index 1d49c2bf..4e3de939 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/CompositeGaussianQuadrature.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/CompositeGaussianQuadrature.java
@@ -1,87 +1,90 @@
package pulse.problem.schemes.rte.dom;
+import java.io.Serializable;
import pulse.math.LegendrePoly;
import pulse.math.MathUtils;
/**
- * A composite Gaussian quadrature for numerical evaluation of the scattering integral in
- * one-dimensional heat transfer.
+ * A composite Gaussian quadrature for numerical evaluation of the scattering
+ * integral in one-dimensional heat transfer.
+ *
* @author Teymur Aliev, Vadim Zborovskii, Artem Lunev
*
*/
-
-public class CompositeGaussianQuadrature {
-
- private LegendrePoly poly;
-
- private double[] roots;
-
- private double[] nodes;
- private double[] weights;
-
- private int n;
-
- /**
- * Constructs a composite Gaussian quadrature for an even {@code n}
- * @param n an even integer
- */
-
- public CompositeGaussianQuadrature(final int n) {
- if(n % 2 != 0)
- throw new IllegalArgumentException(n + " is odd. Even number expected.");
- this.n = n;
- poly = new LegendrePoly(n / 2);
- roots = poly.roots();
- nodes();
- weights();
- }
-
- private void nodes() {
-
- nodes = new double[n];
- weights = new double[n];
-
- for (int i = 0; i < n / 2; i++) {
-
- nodes[i] = 0.5 * (1.0 + roots[i]);
- nodes[i + n / 2] = -0.5 * (1.0 + roots[i]);
-
- }
-
- }
-
- /**
- * Calculates the Gaussian weights. Uses the formula by Abramowitz & Stegun
- * (Abramowitz & Stegun 1972, p. 887))
- */
-
- private void weights() {
- double denominator = 1;
-
- for (int i = 0; i < n / 2; i++) {
- denominator = (1 - roots[i] * roots[i]) * MathUtils.fastPowLoop(poly.derivative(roots[i]), 2);
- weights[i] = 1.0 / denominator;
- weights[i + n / 2] = weights[i];
- }
-
- }
-
- /**
- * The weights of the composite quadrature.
- * @return the weights
- */
-
- public double[] getWeights() {
- return weights;
- }
-
- /**
- * The cosine nodes of the composite quadrature.
- * @return the cosine nodes
- */
-
- public double[] getNodes() {
- return nodes;
- }
-
-}
\ No newline at end of file
+public class CompositeGaussianQuadrature implements Serializable {
+
+ private static final long serialVersionUID = 780827333372523309L;
+
+ private LegendrePoly poly;
+
+ private double[] roots;
+
+ private double[] nodes;
+ private double[] weights;
+
+ private int n;
+
+ /**
+ * Constructs a composite Gaussian quadrature for an even {@code n}
+ *
+ * @param n an even integer
+ */
+ public CompositeGaussianQuadrature(final int n) {
+ if (n % 2 != 0) {
+ throw new IllegalArgumentException(n + " is odd. Even number expected.");
+ }
+ this.n = n;
+ poly = new LegendrePoly(n / 2);
+ roots = poly.roots();
+ nodes();
+ weights();
+ }
+
+ private void nodes() {
+
+ nodes = new double[n];
+ weights = new double[n];
+
+ for (int i = 0; i < n / 2; i++) {
+
+ nodes[i] = 0.5 * (1.0 + roots[i]);
+ nodes[i + n / 2] = -0.5 * (1.0 + roots[i]);
+
+ }
+
+ }
+
+ /**
+ * Calculates the Gaussian weights. Uses the formula by Abramowitz & Stegun
+ * (Abramowitz & Stegun 1972, p. 887))
+ */
+ private void weights() {
+ double denominator = 1;
+
+ for (int i = 0; i < n / 2; i++) {
+ denominator = (1 - roots[i] * roots[i]) * MathUtils.fastPowLoop(poly.derivative(roots[i]), 2);
+ weights[i] = 1.0 / denominator;
+ weights[i + n / 2] = weights[i];
+ }
+
+ }
+
+ /**
+ * The weights of the composite quadrature.
+ *
+ * @return the weights
+ */
+ public double[] getWeights() {
+ return weights;
+ }
+
+ /**
+ * The cosine nodes of the composite quadrature.
+ *
+ * @return the cosine nodes
+ */
+ public double[] getNodes() {
+ return nodes;
+ }
+
+}
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 00000000..5dfccb4d
--- /dev/null
+++ b/src/main/java/pulse/problem/schemes/rte/dom/CornetteSchanksPF.java
@@ -0,0 +1,44 @@
+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 static final long serialVersionUID = -4371291780762389604L;
+ 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));
+ }
+
+}
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 d16237a0..0e3e5d47 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/DiscreteOrdinatesMethod.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/DiscreteOrdinatesMethod.java
@@ -6,8 +6,9 @@
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.ThermoOpticalProperties;
+import pulse.problem.statements.model.ThermoOpticalProperties;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
import pulse.properties.Property;
@@ -20,147 +21,157 @@
* solve to RTE.
*
*/
-
public class DiscreteOrdinatesMethod extends RadiativeTransferSolver {
- private InstanceDescriptor integratorDescriptor = new InstanceDescriptor(
- "Integrator selector", AdaptiveIntegrator.class);
- private InstanceDescriptor iterativeSolverSelector = new InstanceDescriptor(
- "Iterative solver selector", IterativeSolver.class);
- private InstanceDescriptor phaseFunctionSelector = new InstanceDescriptor(
- "Phase function selector", PhaseFunction.class);
-
- private AdaptiveIntegrator integrator;
- private IterativeSolver iterativeSolver;
-
- /**
- * Constructs a discrete ordinates solver using the parameters (emissivity,
- * scattering albedo and optical thickness) declared by the {@code problem}
- * object.
- *
- * @param problem the coupled problem statement
- * @param grid the heat problem grid
- */
-
- public DiscreteOrdinatesMethod(ParticipatingMedium problem, Grid grid) {
- super();
- var properties = (ThermoOpticalProperties)problem.getProperties();
- setFluxes(new FluxesAndExplicitDerivatives(grid.getGridDensity(), properties.getOpticalThickness()));
-
- var discrete = new Discretisation(problem);
-
- integratorDescriptor.setSelectedDescriptor(TRBDF2.class.getSimpleName());
- setIntegrator(integratorDescriptor.newInstance(AdaptiveIntegrator.class, discrete));
-
- iterativeSolverSelector.setSelectedDescriptor(FixedIterations.class.getSimpleName());
- setIterativeSolver(iterativeSolverSelector.newInstance(IterativeSolver.class));
-
- phaseFunctionSelector.setSelectedDescriptor(HenyeyGreensteinPF.class.getSimpleName());
- phaseFunctionSelector.addListener(() -> initPhaseFunction(problem, discrete));
- initPhaseFunction(problem, discrete);
-
- init(problem, grid);
-
- integratorDescriptor.addListener(() -> setIntegrator(
- integratorDescriptor.newInstance(AdaptiveIntegrator.class, discrete)));
-
- iterativeSolverSelector
- .addListener(() -> setIterativeSolver(iterativeSolverSelector.newInstance(IterativeSolver.class)));
-
- }
-
- @Override
- public RTECalculationStatus compute(double[] tempArray) {
- integrator.getEmissionFunction().setInterpolation(interpolateTemperatureProfile(tempArray));
-
- var status = iterativeSolver.doIterations(integrator);
-
- if (status == RTECalculationStatus.NORMAL)
- fluxesAndDerivatives(tempArray.length);
-
- fireStatusUpdate(status);
- return status;
- }
-
- private void fluxesAndDerivatives(final int nExclusive) {
- final var interpolation = integrator.getHermiteInterpolator().interpolateOnExternalGrid(nExclusive, integrator);
-
- final double DOUBLE_PI = 2.0 * Math.PI;
- final var discrete = integrator.getDiscretisation();
- var fluxes = (FluxesAndExplicitDerivatives) getFluxes();
-
- for (int i = 0; i < nExclusive; i++) {
- fluxes.setFlux(i, DOUBLE_PI * discrete.firstMoment(interpolation[0], i));
- fluxes.setFluxDerivative(i, -DOUBLE_PI * discrete.firstMoment(interpolation[1], i));
- }
- }
-
- @Override
- public String getDescriptor() {
- return "Discrete Ordinates Method (DOM)";
- }
-
- @Override
- public void init(ParticipatingMedium problem, Grid grid) {
- super.init(problem, grid);
- initPhaseFunction(problem, integrator.getDiscretisation());
- integrator.init(problem);
- integrator.getPhaseFunction().init(problem);
- }
-
- @Override
- public List listedTypes() {
- List list = super.listedTypes();
- list.add(integratorDescriptor);
- list.add(iterativeSolverSelector);
- list.add(phaseFunctionSelector);
- return list;
- }
-
- public AdaptiveIntegrator getIntegrator() {
- return integrator;
- }
-
- public InstanceDescriptor getIntegratorDescriptor() {
- return integratorDescriptor;
- }
-
- public void setIntegrator(AdaptiveIntegrator integrator) {
- this.integrator = integrator;
- integrator.setParent(this);
- }
-
- public IterativeSolver getIterativeSolver() {
- return iterativeSolver;
- }
-
- public InstanceDescriptor getIterativeSolverSelector() {
- return iterativeSolverSelector;
- }
-
- public void setIterativeSolver(IterativeSolver solver) {
- this.iterativeSolver = solver;
- solver.setParent(this);
- }
-
- public InstanceDescriptor getPhaseFunctionSelector() {
- return phaseFunctionSelector;
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + " : " + integrator.toString() + " ; " + iterativeSolver.toString();
- }
-
- @Override
- 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);
- integrator.setPhaseFunction(pf);
- pf.init(problem);
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = 2881363894773388976L;
+ private InstanceDescriptor integratorDescriptor = new InstanceDescriptor(
+ "Integrator selector", AdaptiveIntegrator.class);
+ private InstanceDescriptor iterativeSolverSelector = new InstanceDescriptor(
+ "Iterative solver selector", IterativeSolver.class);
+ private InstanceDescriptor phaseFunctionSelector = new InstanceDescriptor(
+ "Phase function selector", PhaseFunction.class);
+
+ private AdaptiveIntegrator integrator;
+ private IterativeSolver iterativeSolver;
+
+ /**
+ * Constructs a discrete ordinates solver using the parameters (emissivity,
+ * scattering albedo and optical thickness) declared by the {@code problem}
+ * object.
+ *
+ * @param problem the coupled problem statement
+ * @param grid the heat problem grid
+ */
+ public DiscreteOrdinatesMethod(ParticipatingMedium problem, Grid grid) {
+ super();
+ var properties = (ThermoOpticalProperties) problem.getProperties();
+ setFluxes(new FluxesAndExplicitDerivatives(grid.getGridDensity(), properties.getOpticalThickness()));
+
+ var discrete = new Discretisation(properties);
+
+ integratorDescriptor.setSelectedDescriptor(TRBDF2.class.getSimpleName());
+ setIntegrator(integratorDescriptor.newInstance(AdaptiveIntegrator.class, discrete));
+
+ iterativeSolverSelector.setSelectedDescriptor(FixedIterations.class.getSimpleName());
+ setIterativeSolver(iterativeSolverSelector.newInstance(IterativeSolver.class));
+
+ phaseFunctionSelector.setSelectedDescriptor(HenyeyGreensteinPF.class.getSimpleName());
+ initPhaseFunction(properties, discrete);
+
+ init(problem, grid);
+
+ integratorDescriptor.addListener(() -> setIntegrator(
+ integratorDescriptor.newInstance(AdaptiveIntegrator.class, discrete))
+ );
+
+ iterativeSolverSelector
+ .addListener(() -> setIterativeSolver(iterativeSolverSelector.newInstance(IterativeSolver.class)));
+
+ phaseFunctionSelector.addListener(() -> initPhaseFunction(properties, discrete));
+ }
+
+ @Override
+ public RTECalculationStatus compute(double[] tempArray) {
+ integrator.getEmissionFunction().setInterpolation(interpolateTemperatureProfile(tempArray));
+
+ var status = iterativeSolver.doIterations(integrator);
+
+ if (status == RTECalculationStatus.NORMAL) {
+ fluxesAndDerivatives(tempArray.length);
+ }
+
+ fireStatusUpdate(status);
+ return status;
+ }
+
+ private void fluxesAndDerivatives(final int nExclusive) {
+ final var interpolation = integrator.getHermiteInterpolator()
+ .interpolateOnExternalGrid(nExclusive, integrator);
+
+ final double DOUBLE_PI = 2.0 * Math.PI;
+ final var discrete = integrator.getDiscretisation();
+ var fluxes = (FluxesAndExplicitDerivatives) getFluxes();
+
+ 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));
+ }
+
+ }
+
+ @Override
+ public String getDescriptor() {
+ return "Discrete Ordinates Method (DOM)";
+ }
+
+ @Override
+ public void init(ParticipatingMedium problem, Grid grid) {
+ super.init(problem, grid);
+ var top = (ThermoOpticalProperties) problem.getProperties();
+ initPhaseFunction(top,
+ integrator.getDiscretisation());
+ integrator.init(problem);
+ integrator.getPhaseFunction().init(top);
+ }
+
+ @Override
+ public List listedTypes() {
+ List list = super.listedTypes();
+ list.add(integratorDescriptor);
+ list.add(iterativeSolverSelector);
+ list.add(phaseFunctionSelector);
+ return list;
+ }
+
+ public final AdaptiveIntegrator getIntegrator() {
+ return integrator;
+ }
+
+ public final InstanceDescriptor getIntegratorDescriptor() {
+ return integratorDescriptor;
+ }
+
+ public final void setIntegrator(AdaptiveIntegrator integrator) {
+ this.integrator = integrator;
+ integrator.setParent(this);
+ firePropertyChanged(this, integratorDescriptor);
+ }
+
+ public final IterativeSolver getIterativeSolver() {
+ return iterativeSolver;
+ }
+
+ public final InstanceDescriptor getIterativeSolverSelector() {
+ return iterativeSolverSelector;
+ }
+
+ public final void setIterativeSolver(IterativeSolver solver) {
+ this.iterativeSolver = solver;
+ solver.setParent(this);
+ firePropertyChanged(this, iterativeSolverSelector);
+ }
+
+ public final InstanceDescriptor getPhaseFunctionSelector() {
+ return phaseFunctionSelector;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " : " + integrator.toString() + " ; " + iterativeSolver.toString();
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ // intentionally left blank
+ }
+
+ private void initPhaseFunction(ThermoOpticalProperties top, Discretisation discrete) {
+ var pf = phaseFunctionSelector.newInstance(PhaseFunction.class, top, discrete);
+ integrator.setPhaseFunction(pf);
+ 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 cb965541..e81389a9 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/DiscreteQuantities.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/DiscreteQuantities.java
@@ -1,96 +1,99 @@
package pulse.problem.schemes.rte.dom;
+import java.io.Serializable;
+
/**
- * Defines the main quantities calculated within the discrete ordinates method. This
- * includes the various intensity and flux arrays used internally by the integrators.
+ * Defines the main quantities calculated within the discrete ordinates method.
+ * This includes the various intensity and flux arrays used internally by the
+ * integrators.
*/
+public class DiscreteQuantities implements Serializable {
+
+ private static final long serialVersionUID = -3997479317699236996L;
+ private double[][] I;
+ private double[][] Ik;
+ private double[][] f;
+ private double[][] fk;
+ private double[] qLast;
+
+ /**
+ * Constructs a set of quantities based on the specified spatial and angular
+ * discretisation.
+ *
+ * @param gridDensity the DOM grid density
+ * @param ordinates the number of angular nodes
+ */
+ public DiscreteQuantities(int gridDensity, int ordinates) {
+ init(gridDensity, 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];
+ fk = new double[gridDensity + 1][ordinates];
+ qLast = new double[ordinates];
+ }
+
+ protected void store() {
+ final int n = I.length;
+ final int m = I[0].length;
-class DiscreteQuantities {
-
- private double[][] I;
- private double[][] Ik;
- private double[][] f;
- private double[][] fk;
- private double[] qLast;
-
- /**
- * Constructs a set of quantities based on the specified
- * spatial and angular discretisation.
- * @param gridDensity the DOM grid density
- * @param ordinates the number of angular nodes
- */
-
- public DiscreteQuantities(int gridDensity, int ordinates) {
- init(gridDensity, ordinates);
- }
-
- public void init(int gridDensity, int ordinates) {
- I = new double[gridDensity + 1][ordinates];
- f = new double[gridDensity + 1][ordinates];
- Ik = new double[gridDensity + 1][ordinates];
- fk = new double[gridDensity + 1][ordinates];
- qLast = new double[ordinates];
- }
-
- public void store() {
- final int n = I.length;
- final int m = I[0].length;
-
- Ik = new double[n][m];
- fk = new double[n][m];
-
- /*
+ Ik = new double[n][m];
+ fk = new double[n][m];
+
+ /*
* store k-th components
- */
- for (int j = 0; j < Ik.length; j++) {
- System.arraycopy(I[j], 0, Ik[j], 0, Ik[0].length);
- System.arraycopy(f[j], 0, fk[j], 0, fk[0].length);
- }
-
- }
-
- public double[][] getIntensities() {
- return I;
- }
-
- public double[][] getDerivatives() {
- return f;
- }
-
- public double getQLast(int i) {
- return qLast[i];
- }
-
- public void setQLast(int i, double q) {
- this.qLast[i] = q;
- }
-
- public double getDerivative(int i, int j) {
- return f[i][j];
- }
-
- public void setDerivative(int i, int j, double f) {
- this.f[i][j] = f;
- }
-
- public double getStoredIntensity(final int i, final int j) {
- return Ik[i][j];
- }
-
- public double getStoredDerivative(final int i, final int j) {
- return fk[i][j];
- }
-
- public void setStoredDerivative(final int i, final int j, final double f) {
- this.f[i][j] = f;
- }
-
- public double getIntensity(int i, int j) {
- return I[i][j];
- }
-
- public void setIntensity(int i, int j, double value) {
- I[i][j] = value;
- }
-
-}
\ No newline at end of file
+ */
+ for (int j = 0; j < Ik.length; j++) {
+ System.arraycopy(I[j], 0, Ik[j], 0, Ik[0].length);
+ System.arraycopy(f[j], 0, fk[j], 0, fk[0].length);
+ }
+
+ }
+
+ public final double[][] getIntensities() {
+ return I;
+ }
+
+ public final double[][] getDerivatives() {
+ return f;
+ }
+
+ protected final double getQLast(int i) {
+ return qLast[i];
+ }
+
+ protected final void setQLast(int i, double q) {
+ this.qLast[i] = q;
+ }
+
+ public final double getDerivative(int i, int j) {
+ return f[i][j];
+ }
+
+ public final void setDerivative(int i, int j, double f) {
+ this.f[i][j] = f;
+ }
+
+ public final double getStoredIntensity(final int i, final int j) {
+ return Ik[i][j];
+ }
+
+ public final double getStoredDerivative(final int i, final int j) {
+ return fk[i][j];
+ }
+
+ public final void setStoredDerivative(final int i, final int j, final double f) {
+ this.f[i][j] = f;
+ }
+
+ public final double getIntensity(int i, int j) {
+ return I[i][j];
+ }
+
+ 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 2521c5dc..91be63d3 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/Discretisation.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/Discretisation.java
@@ -2,14 +2,11 @@
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.ThermoOpticalProperties;
+import pulse.problem.statements.model.ThermoOpticalProperties;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
import pulse.properties.Property;
@@ -24,265 +21,260 @@
* and intensities based on the discrete ordinates method.
*
*/
-
public class Discretisation extends PropertyHolder {
- private DiscreteQuantities quantities;
- private StretchedGrid grid;
- private OrdinateSet ordinates;
- private DiscreteSelector quadSelector;
-
- private double emissivity;
- private double boundaryFluxFactor;
-
- /**
- * Constructs a {@code DiscreteIntensities} with the default {@code OrdinateSet}
- * and a new uniform grid.
- *
- * @param problem the problem statement
- */
-
- public Discretisation(ParticipatingMedium problem) {
-
- quadSelector = new DiscreteSelector<>(QuadratureReader.getInstance(), "/quadratures/",
- "Quadratures.list");
- quadSelector.setDefaultSelection(OrdinateSet.DEFAULT_SET);
- ordinates = quadSelector.getDefaultSelection();
- quadSelector.addListener(() -> {
- ordinates = (OrdinateSet)quadSelector.getValue();
- quantities.init(grid.getDensity(), ordinates.getTotalNodes());
- });
-
- 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());
- }
-
- /**
- * Calculates the incident radiation ∑iwi
- * Iij, which is the zeroth moment of the intensities. The
- * calculation uses the symmetry of the quadrature weights for positive and
- * negativ nodes.
- *
- * @param j spatial index
- * @return the incident radiation at {@code j}
- */
-
- public double incidentRadation(final int j) {
- double integral = 0;
-
- final int nHalf = ordinates.getFirstNegativeNode();
- final int nStart = ordinates.getFirstPositiveNode();
-
- // uses symmetry
- for (int i = nStart; i < nHalf; i++) {
- integral += ordinates.getWeight(i)
- * (quantities.getIntensity(j, i) + quantities.getIntensity(j, i + nHalf));
- }
-
- if (ordinates.hasZeroNode())
- integral += ordinates.getWeight(0) * quantities.getIntensity(j, 0);
-
- return integral;
- }
-
- /**
- * Calculates the incident radiation ∑iwi
- * Iij, by performing simple summation for node points
- * between {@code startInclusive} and {@code endExclusive}.
- *
- * @param j spatial index
- * @param startInclusive lower bound for summation
- * @param endExclusive upper bound (exclusive) for summation
- * @return the partial incident radiation at {@code j}
- * @see pulse.problem.schemes.rte.dom.LinearAnisotropicPF
- */
-
- public double incidentRadiation(final int j, final int startInclusive, final int endExclusive) {
- double integral = 0;
-
- for (int i = startInclusive; i < endExclusive; i++) {
- integral += ordinates.getWeight(i) * quantities.getIntensity(j, i);
- }
-
- return integral;
-
- }
-
- /**
- * Calculates the first moment
- * ∑iwiμiextij,
- * which can be applied e.g. for flux or flux derivative calculation. The
- * calculation uses the symmetry of the quadrature weights for positive and
- * negativ nodes.
- *
- * @param j spatial index
- * @return the first moment at {@code j}
- */
-
- public double firstMoment(final double[][] ext, final int j) {
- double integral = 0;
-
- final int nHalf = ordinates.getFirstNegativeNode();
- final int nStart = ordinates.getFirstPositiveNode();
-
- // uses symmetry
- for (int i = nStart; i < nHalf; i++) {
- integral += ordinates.getWeight(i) * (ext[j][i] - ext[j][i + nHalf]) * ordinates.getNode(i);
- }
-
- return integral;
- }
-
- /**
- * Calculates the net flux at {@code j}.
- *
- * @param j the spatial coordinate
- * @return the flux
- * @see firstMoment(double[][],int)
- */
-
- public double flux(final int j) {
- return firstMoment(quantities.getIntensities(), j);
- }
-
- /**
- * Calculates the partial flux by performing a simple summation bounded by the
- * arguments.
- *
- * @param j the spatial index
- * @param startInclusive node index lower bound
- * @param endExclusive node index upper bound (exclusive)
- * @return the partial flux
- */
-
- public double flux(final int j, final int startInclusive, final int endExclusive) {
- double integral = 0;
-
- for (int i = startInclusive; i < endExclusive; i++) {
- integral += ordinates.getWeight(i) * quantities.getIntensity(j, i) * ordinates.getNode(i);
- }
-
- return integral;
- }
-
- /**
- * Calculates the flux at the left boundary using an alternative formula.
- *
- * @param emissionFunction the emission function
- * @return the net flux at the left boundary
- */
-
- public double fluxLeft(final BlackbodySpectrum emissionFunction) {
- final int nHalf = ordinates.getFirstNegativeNode();
- return emissivity * PI * (emissionFunction.radianceAt(0.0) + 2.0 * flux(0, nHalf, ordinates.getTotalNodes()));
- }
-
- /**
- * Calculates the flux at the right boundary using an alternative formula.
- *
- * @param emissionFunction the emission function
- * @return the net flux at the right boundary
- */
-
- public double fluxRight(final BlackbodySpectrum emissionFunction) {
- final int nHalf = ordinates.getFirstNegativeNode();
- final int nStart = ordinates.getFirstPositiveNode();
- return -emissivity * PI
- * (emissionFunction.radianceAt(grid.getDimension()) - 2.0 * flux(grid.getDensity(), nStart, nHalf));
- }
-
- /**
- * Calculates the reflected intensity (positive angles, first half of indices)
- * at the left boundary (τ = 0).
- *
- * @param ef the emission function
- */
-
- public void intensitiesLeftBoundary(final BlackbodySpectrum ef) {
- final int nHalf = ordinates.getFirstNegativeNode();
- final int nStart = ordinates.getFirstPositiveNode();
-
- for (int i = nStart; i < nHalf; i++) {
- // for positive streams
- quantities.setIntensity(0, i, ef.radianceAt(0.0) - boundaryFluxFactor * fluxLeft(ef));
- }
-
- }
-
- /**
- * Calculates the reflected intensity (negative angles, second half of indices)
- * at the right boundary (τ = τ0).
- *
- * @param ef the emission function
- */
-
- public void intensitiesRightBoundary(final BlackbodySpectrum ef) {
-
- final int N = grid.getDensity();
- final int nHalf = ordinates.getFirstNegativeNode();
- final double tau0 = grid.getDimension();
-
- for (int i = nHalf; i < ordinates.getTotalNodes(); i++) {
- // for negative streams
- quantities.setIntensity(N, i, ef.radianceAt(tau0) + boundaryFluxFactor * fluxRight(ef));
- }
-
- }
-
- protected void setEmissivity(double emissivity) {
- this.emissivity = emissivity;
- boundaryFluxFactor = (1.0 - emissivity) / (emissivity * PI);
- }
-
- public OrdinateSet getOrdinates() {
- return ordinates;
- }
-
- public void setOrdinateSet(OrdinateSet set) {
- this.ordinates = set;
- quantities.init(grid.getDensity(), ordinates.getTotalNodes());
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- // intentionally left blank
- }
-
- @Override
- public List listedTypes() {
- return new ArrayList(Arrays.asList(quadSelector));
- }
-
- @Override
- public String getDescriptor() {
- return "Discretisation";
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("( ");
- sb.append("Quadrature: " + ordinates.getName() + " ; ");
- sb.append("Grid: " + grid.toString());
- return sb.toString();
- }
-
- public StretchedGrid getGrid() {
- return grid;
- }
-
- public void setGrid(StretchedGrid grid) {
- this.grid = grid;
- this.grid.setParent(this);
- }
-
- public DiscreteQuantities getQuantities() {
- return quantities;
- }
-
- public DiscreteSelector getQuadratureSelector() {
- return quadSelector;
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = 7069987686586911101L;
+ private DiscreteQuantities quantities;
+ private StretchedGrid grid;
+ private OrdinateSet ordinates;
+ private DiscreteSelector quadSelector;
+
+ private double emissivity;
+ private double boundaryFluxFactor;
+
+ /**
+ * Constructs a {@code DiscreteIntensities} with the default
+ * {@code OrdinateSet} and a new uniform grid.
+ *
+ * @param properties
+ */
+ public Discretisation(ThermoOpticalProperties properties) {
+
+ quadSelector = new DiscreteSelector<>(QuadratureReader.getInstance(), "/quadratures/",
+ "Quadratures.list");
+ quadSelector.setDefaultSelection(OrdinateSet.DEFAULT_SET);
+ ordinates = quadSelector.getDefaultSelection();
+ quadSelector.addListener(() -> {
+ ordinates = (OrdinateSet) quadSelector.getValue();
+ quantities.init(grid.getDensity(), ordinates.getTotalNodes());
+ this.firePropertyChanged(this, quadSelector);
+ });
+
+ setGrid(new StretchedGrid((double) properties.getOpticalThickness().getValue()));
+ quantities = new DiscreteQuantities(grid.getDensity(), ordinates.getTotalNodes());
+ setEmissivity((double) properties.getEmissivity().getValue());
+ }
+
+ /**
+ * Calculates the incident radiation
+ * ∑iwi
+ * Iij, which is the zeroth moment of the intensities.
+ * The calculation uses the symmetry of the quadrature weights for positive
+ * and negativ nodes.
+ *
+ * @param j spatial index
+ * @return the incident radiation at {@code j}
+ */
+ public double incidentRadation(final int j) {
+ double integral = 0;
+
+ final int nHalf = ordinates.getFirstNegativeNode();
+ final int nStart = ordinates.getFirstPositiveNode();
+
+ // uses symmetry
+ for (int i = nStart; i < nHalf; i++) {
+ integral += ordinates.getWeight(i)
+ * (quantities.getIntensity(j, i) + quantities.getIntensity(j, i + nHalf));
+ }
+
+ if (ordinates.hasZeroNode()) {
+ integral += ordinates.getWeight(0) * quantities.getIntensity(j, 0);
+ }
+
+ return integral;
+ }
+
+ /**
+ * Calculates the incident radiation
+ * ∑iwi
+ * Iij, by performing simple summation for node points
+ * between {@code startInclusive} and {@code endExclusive}.
+ *
+ * @param j spatial index
+ * @param startInclusive lower bound for summation
+ * @param endExclusive upper bound (exclusive) for summation
+ * @return the partial incident radiation at {@code j}
+ * @see pulse.problem.schemes.rte.dom.LinearAnisotropicPF
+ */
+ public double incidentRadiation(final int j, final int startInclusive, final int endExclusive) {
+ double integral = 0;
+
+ for (int i = startInclusive; i < endExclusive; i++) {
+ integral += ordinates.getWeight(i) * quantities.getIntensity(j, i);
+ }
+
+ return integral;
+
+ }
+
+ /**
+ * Calculates the first moment
+ * ∑iwiμiextij,
+ * which can be applied e.g. for flux or flux derivative calculation. The
+ * calculation uses the symmetry of the quadrature weights for positive and
+ * negativ nodes.
+ *
+ * @param j spatial index
+ * @return the first moment at {@code j}
+ */
+ public double firstMoment(final double[][] ext, final int j) {
+ double integral = 0;
+
+ final int nHalf = ordinates.getFirstNegativeNode();
+ final int nStart = ordinates.getFirstPositiveNode();
+
+ // uses symmetry
+ for (int i = nStart; i < nHalf; i++) {
+ integral += ordinates.getWeight(i) * (ext[j][i] - ext[j][i + nHalf]) * ordinates.getNode(i);
+ }
+
+ return integral;
+ }
+
+ /**
+ * Calculates the net flux at {@code j}.
+ *
+ * @param j the spatial coordinate
+ * @return the flux
+ * @see firstMoment(double[][],int)
+ */
+ public double flux(final int j) {
+ return firstMoment(quantities.getIntensities(), j);
+ }
+
+ /**
+ * Calculates the partial flux by performing a simple summation bounded by
+ * the arguments.
+ *
+ * @param j the spatial index
+ * @param startInclusive node index lower bound
+ * @param endExclusive node index upper bound (exclusive)
+ * @return the partial flux
+ */
+ public double flux(final int j, final int startInclusive, final int endExclusive) {
+ double integral = 0;
+
+ for (int i = startInclusive; i < endExclusive; i++) {
+ integral += ordinates.getWeight(i) * quantities.getIntensity(j, i) * ordinates.getNode(i);
+ }
+
+ return integral;
+ }
+
+ /**
+ * Calculates the flux at the left boundary using an alternative formula.
+ *
+ * @param emissionFunction the emission function
+ * @return the net flux at the left boundary
+ */
+ public double fluxLeft(final BlackbodySpectrum emissionFunction) {
+ final int nHalf = ordinates.getFirstNegativeNode();
+ return emissivity * PI * (emissionFunction.radianceAt(0.0) + 2.0 * flux(0, nHalf, ordinates.getTotalNodes()));
+ }
+
+ /**
+ * Calculates the flux at the right boundary using an alternative formula.
+ *
+ * @param emissionFunction the emission function
+ * @return the net flux at the right boundary
+ */
+ public double fluxRight(final BlackbodySpectrum emissionFunction) {
+ final int nHalf = ordinates.getFirstNegativeNode();
+ final int nStart = ordinates.getFirstPositiveNode();
+ return -emissivity * PI
+ * (emissionFunction.radianceAt(grid.getDimension()) - 2.0 * flux(grid.getDensity(), nStart, nHalf));
+ }
+
+ /**
+ * Calculates the reflected intensity (positive angles, first half of
+ * indices) at the left boundary (τ = 0).
+ *
+ * @param ef the emission function
+ */
+ public void intensitiesLeftBoundary(final BlackbodySpectrum ef) {
+ final int nHalf = ordinates.getFirstNegativeNode();
+ final int nStart = ordinates.getFirstPositiveNode();
+
+ for (int i = nStart; i < nHalf; i++) {
+ // for positive streams
+ quantities.setIntensity(0, i, ef.radianceAt(0.0) - boundaryFluxFactor * fluxLeft(ef));
+ }
+
+ }
+
+ /**
+ * Calculates the reflected intensity (negative angles, second half of
+ * indices) at the right boundary (τ = τ0).
+ *
+ * @param ef the emission function
+ */
+ public void intensitiesRightBoundary(final BlackbodySpectrum ef) {
+
+ final int N = grid.getDensity();
+ final int nHalf = ordinates.getFirstNegativeNode();
+ final double tau0 = grid.getDimension();
+
+ for (int i = nHalf; i < ordinates.getTotalNodes(); i++) {
+ // for negative streams
+ quantities.setIntensity(N, i, ef.radianceAt(tau0) + boundaryFluxFactor * fluxRight(ef));
+ }
+
+ }
+
+ protected final void setEmissivity(double emissivity) {
+ this.emissivity = emissivity;
+ boundaryFluxFactor = (1.0 - emissivity) / (emissivity * PI);
+ }
+
+ public OrdinateSet getOrdinates() {
+ return ordinates;
+ }
+
+ public void setOrdinateSet(OrdinateSet set) {
+ this.ordinates = set;
+ quantities.init(grid.getDensity(), ordinates.getTotalNodes());
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ // intentionally left blank
+ }
+
+ @Override
+ public List listedTypes() {
+ var list = super.listedTypes();
+ list.add(quadSelector);
+ return list;
+ }
+
+ @Override
+ public String getDescriptor() {
+ return "Discretisation";
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("( ");
+ sb.append("Quadrature: " + ordinates.getName() + " ; ");
+ sb.append("Grid: " + grid.toString());
+ return sb.toString();
+ }
+
+ public StretchedGrid getGrid() {
+ return grid;
+ }
+
+ public final void setGrid(StretchedGrid grid) {
+ this.grid = grid;
+ this.grid.setParent(this);
+ }
+
+ public DiscreteQuantities getQuantities() {
+ return quantities;
+ }
+
+ public DiscreteSelector getQuadratureSelector() {
+ return quadSelector;
+ }
+
+}
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 f8ad0f10..42b98b9a 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/ExplicitRungeKutta.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/ExplicitRungeKutta.java
@@ -8,193 +8,187 @@
import pulse.util.DiscreteSelector;
/**
- * Explicit Runge-Kutta integrator with Hermite interpolation for the solution of one-dimensional radiative transfer problems.
- *
+ * Explicit Runge-Kutta integrator with Hermite interpolation for the solution
+ * of one-dimensional radiative transfer problems.
+ *
* @author Artem Lunev, Vadim Zborovskii
*
*/
-
public class ExplicitRungeKutta extends AdaptiveIntegrator {
- private ButcherTableau tableau;
- private DiscreteSelector tableauSelector;
+ private static final long serialVersionUID = -2478658861611086402L;
+ private ButcherTableau tableau;
+ private DiscreteSelector tableauSelector;
- public ExplicitRungeKutta(Discretisation intensities) {
- super(intensities);
-
- tableauSelector = new DiscreteSelector<>(ButcherTableauReader.getInstance(), "/solvers/",
- "Solvers.list");
- tableauSelector.setDefaultSelection(ButcherTableau.DEFAULT_TABLEAU);
- tableau = tableauSelector.getDefaultSelection();
- tableauSelector.addListener(() -> tableau = (ButcherTableau)tableauSelector.getValue());
- }
+ public ExplicitRungeKutta(Discretisation intensities) {
+ super(intensities);
- @Override
- public Vector[] step(final int j, final double sign) {
+ tableauSelector = new DiscreteSelector<>(ButcherTableauReader.getInstance(), "/solvers/",
+ "Solvers.list");
+ tableauSelector.setDefaultSelection(ButcherTableau.DEFAULT_TABLEAU);
+ tableau = tableauSelector.getDefaultSelection();
+ tableauSelector.addListener(() -> setButcherTableau((ButcherTableau) tableauSelector.getValue()));
+ }
- var quantities = getDiscretisation().getQuantities();
-
- final var grid = getDiscretisation().getGrid();
- final var ordinates = getDiscretisation().getOrdinates();
+ @Override
+ public Vector[] step(final int j, final double sign) {
- final double h = grid.step(j, sign);
- final double hSigned = h * sign;
- final double t = grid.getNode(j);
+ var quantities = getDiscretisation().getQuantities();
- final int nPositiveStart = ordinates.getFirstPositiveNode();
- final int nNegativeStart = ordinates.getFirstNegativeNode();
+ final var grid = getDiscretisation().getGrid();
+ final var ordinates = getDiscretisation().getOrdinates();
- var hermite = getHermiteInterpolator();
-
- hermite.a = t;
- hermite.bMinusA = hSigned;
+ final double h = grid.step(j, sign);
+ final double hSigned = h * sign;
+ final double t = grid.getNode(j);
- /*
- * Indices of outward (n1 to n2) and inward (> n3) intensities
- */
+ final int nPositiveStart = ordinates.getFirstPositiveNode();
+ final int nNegativeStart = ordinates.getFirstNegativeNode();
- final int n1 = sign > 0 ? nPositiveStart : nNegativeStart; // either first positive index (e.g. 0) or first
- // negative (n/2)
- final int n2 = sign > 0 ? nNegativeStart : ordinates.getTotalNodes(); // either first negative index (n/2) or
- // n
- final int n3 = ordinates.getTotalNodes() - n2; // either nNegativeStart or 0
- final int nH = n2 - n1;
+ var hermite = getHermiteInterpolator();
- var error = new double[nH];
- var iOutward = new double[nH];
- var iInward = new double[nH];
+ hermite.a = t;
+ hermite.bMinusA = hSigned;
- int stages = tableau.numberOfStages();
+ /*
+ * Indices of outward (n1 to n2) and inward (> n3) intensities
+ */
+ final int n1 = sign > 0 ? nPositiveStart : nNegativeStart; // either first positive index (e.g. 0) or first
+ // negative (n/2)
+ final int n2 = sign > 0 ? nNegativeStart : ordinates.getTotalNodes(); // either first negative index (n/2) or
+ // n
+ final int n3 = ordinates.getTotalNodes() - n2; // either nNegativeStart or 0
+ final int nH = n2 - n1;
- var q = new double[nH][stages]; // first index - cosine node, second index - stage
+ var error = new double[nH];
+ var iOutward = new double[nH];
+ var iInward = new double[nH];
- double bDotQ;
- double sum;
+ int stages = tableau.numberOfStages();
- int increment = (int) (1 * sign);
+ var q = new double[nH][stages]; // first index - cosine node, second index - stage
- /*
- * RK Explicit (Embedded)
- */
+ double bDotQ;
+ double sum;
- /*
- * First stage
- */
+ int increment = (int) (1 * sign);
- if (tableau.isFSAL() && ! isFirstRun() ) { // if FSAL
+ /*
+ * RK Explicit (Embedded)
+ */
- for (int l = n1; l < n2; l++) {
- q[l - n1][0] = quantities.getQLast(l - n1); // assume first stage is the last stage of last step
- }
+ /*
+ * First stage
+ */
+ if (tableau.isFSAL() && !isFirstRun()) { // if FSAL
- } else { // if not FSAL or on first run
+ for (int l = n1; l < n2; l++) {
+ q[l - n1][0] = quantities.getQLast(l - n1); // assume first stage is the last stage of last step
+ }
- for (int l = n1; l < n2; l++) {
- q[l - n1][0] = derivative(l, j, t, quantities.getIntensity(j, l));
- }
+ } else { // if not FSAL or on first run
- setFirstRun(false);
+ for (int l = n1; l < n2; l++) {
+ q[l - n1][0] = derivative(l, j, t, quantities.getIntensity(j, l));
+ }
- }
+ setFirstRun(false);
- // in any case
+ }
- for (int l = n1; l < n2; l++) {
- quantities.setDerivative(j, l, q[l - n1][0]); // store derivative for inward intensities
- error[l - n1] = (tableau.getInterpolator().get(0) - tableau.getEstimator().get(0)) * q[l - n1][0] * hSigned;
- }
+ // in any case
+ for (int l = n1; l < n2; l++) {
+ quantities.setDerivative(j, l, q[l - n1][0]); // store derivative for inward intensities
+ error[l - n1] = (tableau.getInterpolator().get(0) - tableau.getEstimator().get(0)) * q[l - n1][0] * hSigned;
+ }
- /*
+ /*
* Next stages
- */
-
- for (int m = 1; m < stages; m++) { // <------- STAGES (1...s)
+ */
+ for (int m = 1; m < stages; m++) { // <------- STAGES (1...s)
- /*
+ /*
* Calculate interpolated (OUTWARD and INWARD) intensities at each stage from m
* = 1 onwards
- */
-
- double tm = t + hSigned * tableau.getC().get(m); // interpolation point for stage m
+ */
+ double tm = t + hSigned * tableau.getC().get(m); // interpolation point for stage m
- for (int l = n1; l < n2; l++) { // find unknown intensities (sum over the outward intensities)
+ 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++) {
+ sum += tableau.getMatrix().get(m, k) * q[l - n1][k];
+ }
- sum = tableau.getMatrix().get(m, 0) * q[l - n1][0];
- for (int k = 1; k < m; k++)
- sum += tableau.getMatrix().get(m, k) * q[l - n1][k];
+ iOutward[l - n1] = quantities.getIntensity(j, l) + hSigned * sum; // outward intensities are simply
+ // found from the
+ // RK explicit expressions
- iOutward[l - n1] = quantities.getIntensity(j, l) + hSigned * sum; // outward intensities are simply
- // found from the
- // RK explicit expressions
-
- /*
+ /*
* INWARD
- */
-
- hermite.y0 = quantities.getIntensity(j, l + n3);
- hermite.y1 = quantities.getIntensity(j + increment, l + n3);
- hermite.d0 = quantities.getDerivative(j, l + n3);
- hermite.d1 = quantities.getDerivative(j + increment, l + n3);
+ */
+ hermite.y0 = quantities.getIntensity(j, l + n3);
+ hermite.y1 = quantities.getIntensity(j + increment, l + n3);
+ hermite.d0 = quantities.getDerivative(j, l + n3);
+ hermite.d1 = quantities.getDerivative(j + increment, l + n3);
- iInward[l - n1] = hermite.interpolate(tm); // inward intensities are interpolated with
- // Hermite polynomials
+ iInward[l - n1] = hermite.interpolate(tm); // inward intensities are interpolated with
+ // Hermite polynomials
- }
+ }
- /*
+ /*
* Derivatives and associated errors at stage m
- */
-
- for (int l = n1; l < n2; l++) {
- q[l - n1][m] = derivative(l, tm, iOutward, iInward, n1, n2);
- quantities.setQLast(l - n1, q[l - n1][m]);
- error[l - n1] += (tableau.getInterpolator().get(m) - tableau.getEstimator().get(m)) * q[l - n1][m]
- * hSigned;
- }
+ */
+ for (int l = n1; l < n2; l++) {
+ q[l - n1][m] = derivative(l, tm, iOutward, iInward, n1, n2);
+ quantities.setQLast(l - n1, q[l - n1][m]);
+ error[l - n1] += (tableau.getInterpolator().get(m) - tableau.getEstimator().get(m)) * q[l - n1][m]
+ * hSigned;
+ }
- }
+ }
- double[] Is = new double[nH];
+ double[] Is = new double[nH];
- /*
+ /*
* Value at next step
- */
-
- for (int l = 0; l < nH; l++) {
- bDotQ = tableau.getInterpolator().dot(new Vector(q[l]));
- Is[l] = quantities.getIntensity(j, l + n1) + bDotQ * hSigned;
- }
-
- return new Vector[] { new Vector(Is), new Vector(error) };
-
- }
-
- public ButcherTableau getButcherTableau() {
- return tableau;
- }
-
- public void setButcherTableau(ButcherTableau coef) {
- this.tableau = coef;
- }
-
- @Override
- public List listedTypes() {
- List list = super.listedTypes();
- list.add(tableauSelector);
- return list;
- }
-
- @Override
- public String toString() {
- return super.toString() + " ; " + tableau;
- }
-
- public DiscreteSelector getTableauSelector() {
- return tableauSelector;
- }
-
-}
\ No newline at end of file
+ */
+ for (int l = 0; l < nH; l++) {
+ bDotQ = tableau.getInterpolator().dot(new Vector(q[l]));
+ Is[l] = quantities.getIntensity(j, l + n1) + bDotQ * hSigned;
+ }
+
+ return new Vector[]{new Vector(Is), new Vector(error)};
+
+ }
+
+ public ButcherTableau getButcherTableau() {
+ return tableau;
+ }
+
+ public void setButcherTableau(ButcherTableau coef) {
+ this.tableau = coef;
+ this.firePropertyChanged(this, tableauSelector);
+ }
+
+ @Override
+ public List listedTypes() {
+ List list = super.listedTypes();
+ list.add(tableauSelector);
+ return list;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " ; " + tableau;
+ }
+
+ public DiscreteSelector getTableauSelector() {
+ return tableauSelector;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/FixedIterations.java b/src/main/java/pulse/problem/schemes/rte/dom/FixedIterations.java
index 3ec1b198..2a9ea834 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/FixedIterations.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/FixedIterations.java
@@ -6,41 +6,43 @@
public class FixedIterations extends IterativeSolver {
- @Override
- public RTECalculationStatus doIterations(AdaptiveIntegrator integrator) {
+ private static final long serialVersionUID = -7308041206602757928L;
- var discrete = integrator.getDiscretisation();
- double relativeError = 100;
+ @Override
+ public RTECalculationStatus doIterations(AdaptiveIntegrator integrator) {
- double qld = 0;
- double qrd = 0;
- double qsum;
+ var discrete = integrator.getDiscretisation();
+ double relativeError = 100;
- int iterations = 0;
+ double qld = 0;
+ double qrd = 0;
+ double qsum;
- RTECalculationStatus status = RTECalculationStatus.NORMAL;
- final var ef = integrator.getEmissionFunction();
+ int iterations = 0;
- for (double ql = 1e8, qr = ql; relativeError > getIterationError()
- && status == RTECalculationStatus.NORMAL; status = sanityCheck(status, ++iterations)) {
- // do the integration
- status = integrator.integrate();
+ RTECalculationStatus status = RTECalculationStatus.NORMAL;
+ final var ef = integrator.getEmissionFunction();
- // get the difference in boundary heat fluxes
- qld = discrete.fluxLeft(ef);
- qrd = discrete.fluxRight(ef);
- qsum = abs(qld - ql) + abs(qrd - qr);
-
- // if the integrator attempted rescaling, last iteration is not valid anymore
- relativeError = integrator.wasRescaled() ? Double.POSITIVE_INFINITY : qsum / (abs(ql) + abs(qr));
-
- //use previous iteration
- ql = qld;
- qr = qrd;
- }
+ for (double ql = 1e8, qr = ql; relativeError > getIterationError()
+ && status == RTECalculationStatus.NORMAL; status = sanityCheck(status, ++iterations)) {
+ // do the integration
+ status = integrator.integrate();
- return status;
+ // get the difference in boundary heat fluxes
+ qld = discrete.fluxLeft(ef);
+ qrd = discrete.fluxRight(ef);
+ qsum = abs(qld - ql) + abs(qrd - qr);
- }
+ // if the integrator attempted rescaling, last iteration is not valid anymore
+ relativeError = integrator.wasRescaled() ? Double.POSITIVE_INFINITY : qsum / (abs(ql) + abs(qr));
-}
\ No newline at end of file
+ //use previous iteration
+ ql = qld;
+ qr = qrd;
+ }
+
+ return status;
+
+ }
+
+}
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 053e9452..12af9709 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/HenyeyGreensteinPF.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/HenyeyGreensteinPF.java
@@ -2,39 +2,37 @@
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.
*
*/
-
public class HenyeyGreensteinPF extends PhaseFunction {
- private double a1;
- private double a2;
- private double b1;
-
- public HenyeyGreensteinPF(ParticipatingMedium medium, Discretisation intensities) {
- super(medium, intensities);
- }
-
- @Override
- public void init(ParticipatingMedium problem) {
- super.init(problem);
- final double anisotropy = getAnisotropyFactor();
- b1 = 2.0 * anisotropy;
- final double aSq = anisotropy * anisotropy;
- a1 = 1.0 - aSq;
- a2 = 1.0 + aSq;
- }
-
- @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;
- return a1 / (f * sqrt(f));
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = -2196189314681832809L;
+ private double a1;
+ private double a2;
+ private double b1;
+
+ public HenyeyGreensteinPF(ThermoOpticalProperties properties, Discretisation intensities) {
+ super(properties, intensities);
+ }
+
+ @Override
+ public void init(ThermoOpticalProperties properties) {
+ super.init(properties);
+ final double anisotropy = getAnisotropyFactor();
+ b1 = 2.0 * anisotropy;
+ final double aSq = anisotropy * anisotropy;
+ a1 = 1.0 - aSq;
+ a2 = 1.0 + aSq;
+ }
+
+ @Override
+ public double function(final int i, final int k) {
+ final double f = a2 - b1 * cosineTheta(i, k);
+ return a1 / (f * sqrt(f));
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/HermiteInterpolator.java b/src/main/java/pulse/problem/schemes/rte/dom/HermiteInterpolator.java
index 18d8f0a7..93c4131d 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/HermiteInterpolator.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/HermiteInterpolator.java
@@ -1,130 +1,134 @@
package pulse.problem.schemes.rte.dom;
+import java.io.Serializable;
+
/**
- * A globally C1 Hermite interpolator used to interpolate intensities and derivatives
- * in discrete ordinates method when solving the radiative transfer equation with a Runge-Kutta
- * solver (either implicit or explicit).
+ * A globally C1 Hermite interpolator used to interpolate intensities
+ * and derivatives in discrete ordinates method when solving the radiative
+ * transfer equation with a Runge-Kutta solver (either implicit or explicit).
+ *
* @author Vadim Zborovskii, Artem Lunev
*
*/
-
-public class HermiteInterpolator {
-
- protected double y1;
- protected double y0;
- protected double d1;
- protected double d0;
-
- protected double a;
- protected double bMinusA;
-
- public void clear() {
- y1 = 0;
- y0 = 0;
- d1 = 0;
- d0 = 0;
- a = 0;
- bMinusA = 0;
- }
-
- /**
- * Interpolates the function at {@code x}
- * @param x the value within the specified bounds
- * @return the interpolated value
- */
-
- public double interpolate(final double x) {
- final double t = (x - a) / bMinusA;
- final double tMinusOne = (t - 1.0);
- return t * t * (3.0 - 2.0 * t) * y1 + tMinusOne * tMinusOne * (1.0 + 2.0 * t) * y0
- + (t * t * tMinusOne * d1 + tMinusOne * tMinusOne * t * d0) * bMinusA;
- }
-
- /**
- * Calculates the derivative of the interpolant at {@code x}
- * @param x the value within the specified bounds
- * @return the derivative of the interpolant
- */
-
- public double derivative(final double x) {
- final double t = (x - a) / bMinusA;
- final double tt1 = t * (t - 1.0);
-
- return 6.0 * tt1 * (y0 - y1) / bMinusA + (t * (3.0 * t - 2.0) * d1 + (3.0 * t * t - 4.0 * t + 1.0) * d0);
- }
-
- @Override
- public String toString() {
- return String.format("%n (%3.6f ; %3.6f), (%3.6f ; %3.6f), (%3.6f, %3.6f)", y0, y1, d0, d1, a, (bMinusA - a));
- }
-
- /**
- * Interpolates intensities and their derivatives w.r.t. tau on EXTERNAL grid
- * points of the heat problem solver based on the derivatives on INTERNAL grid
- * points of DOM solver.
- * @param externalGridSize the number of points in the external grid
- * @param integrator the adaptive integrator
- * @return a three-dimensional array containing the interpolated intensities and derivatives
- */
-
- public double[][][] interpolateOnExternalGrid(final int externalGridSize, AdaptiveIntegrator integrator) {
- final var discrete = integrator.getDiscretisation();
- final var intensities = discrete.getQuantities().getIntensities();
- final var internalGrid = discrete.getGrid();
- final var derivatives = discrete.getQuantities().getDerivatives();
- final int total = discrete.getOrdinates().getTotalNodes();
-
- var iExt = new double[2][externalGridSize][total];
- double t;
-
- final double hx = internalGrid.getDimension() / (externalGridSize - 1.0);
- final int n = internalGrid.getDensity() + 1;
-
- /*
+public class HermiteInterpolator implements Serializable {
+
+ private static final long serialVersionUID = -1973954803574711053L;
+ protected double y1;
+ protected double y0;
+ protected double d1;
+ protected double d0;
+
+ protected double a;
+ protected double bMinusA;
+
+ public void clear() {
+ y1 = 0;
+ y0 = 0;
+ d1 = 0;
+ d0 = 0;
+ a = 0;
+ bMinusA = 0;
+ }
+
+ /**
+ * Interpolates the function at {@code x}
+ *
+ * @param x the value within the specified bounds
+ * @return the interpolated value
+ */
+ public double interpolate(final double x) {
+ final double t = (x - a) / bMinusA;
+ final double tMinusOne = (t - 1.0);
+ return t * t * (3.0 - 2.0 * t) * y1 + tMinusOne * tMinusOne * (1.0 + 2.0 * t) * y0
+ + (t * t * tMinusOne * d1 + tMinusOne * tMinusOne * t * d0) * bMinusA;
+ }
+
+ /**
+ * Calculates the derivative of the interpolant at {@code x}
+ *
+ * @param x the value within the specified bounds
+ * @return the derivative of the interpolant
+ */
+ public double derivative(final double x) {
+ final double t = (x - a) / bMinusA;
+ final double tt1 = t * (t - 1.0);
+
+ return 6.0 * tt1 * (y0 - y1) / bMinusA + (t * (3.0 * t - 2.0) * d1 + (3.0 * t * t - 4.0 * t + 1.0) * d0);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%n (%3.6f ; %3.6f), (%3.6f ; %3.6f), (%3.6f, %3.6f)", y0, y1, d0, d1, a, (bMinusA - a));
+ }
+
+ /**
+ * Interpolates intensities and their derivatives w.r.t. tau on EXTERNAL
+ * grid points of the heat problem solver based on the derivatives on
+ * INTERNAL grid points of DOM solver.
+ *
+ * @param externalGridSize the number of points in the external grid
+ * @param integrator the adaptive integrator
+ * @return a three-dimensional array containing the interpolated intensities
+ * and derivatives
+ */
+ public double[][][] interpolateOnExternalGrid(final int externalGridSize, AdaptiveIntegrator integrator) {
+ final var discrete = integrator.getDiscretisation();
+ final var intensities = discrete.getQuantities().getIntensities();
+ final var internalGrid = discrete.getGrid();
+ final var derivatives = discrete.getQuantities().getDerivatives();
+ final int total = discrete.getOrdinates().getTotalNodes();
+
+ var iExt = new double[2][externalGridSize][total];
+ double t;
+
+ final double hx = internalGrid.getDimension() / (externalGridSize - 1.0);
+ final int n = internalGrid.getDensity() + 1;
+
+ /*
* Loop through the external grid points
- */
- outer: for (int i = 0; i < iExt[0].length; i++) {
- t = i * hx;
+ */
+ outer:
+ for (int i = 0; i < iExt[0].length; i++) {
+ t = i * hx;
- // loops through nodes sorted in ascending order
- for (int j = 0; j < n; j++) {
+ // loops through nodes sorted in ascending order
+ for (int j = 0; j < n; j++) {
- /*
+ /*
* if node is greater than t, then the associated function can be interpolated
* between points f_i and f_i-1, since t lies between nodes n_i and n_i-1
- */
- if (internalGrid.getNode(j) > t) { // nearest points on internal grid have been found -> j - 1 and j
+ */
+ if (internalGrid.getNode(j) > t) { // nearest points on internal grid have been found -> j - 1 and j
- a = internalGrid.getNode(j - 1);
- bMinusA = internalGrid.stepLeft(j);
+ a = internalGrid.getNode(j - 1);
+ bMinusA = internalGrid.stepLeft(j);
- /*
+ /*
* Loops through ordinate set
- */
-
- for (int k = 0; k < total; k++) {
- y0 = intensities[j - 1][k];
- y1 = intensities[j][k];
- d0 = derivatives[j - 1][k];
- d1 = derivatives[j][k];
- iExt[0][i][k] = interpolate(t); // intensity
- iExt[1][i][k] = derivative(t); // derivative
- }
+ */
+ for (int k = 0; k < total; k++) {
+ y0 = intensities[j - 1][k];
+ y1 = intensities[j][k];
+ d0 = derivatives[j - 1][k];
+ d1 = derivatives[j][k];
+ iExt[0][i][k] = interpolate(t); // intensity
+ iExt[1][i][k] = derivative(t); // derivative
+ }
- continue outer; // move to next point t
+ continue outer; // move to next point t
- }
+ }
- }
+ }
- for (int k = 0; k < total; k++) {
- iExt[0][i][k] = intensities[internalGrid.getDensity()][k];
- iExt[1][i][k] = derivatives[internalGrid.getDensity()][k];
- }
+ for (int k = 0; k < total; k++) {
+ iExt[0][i][k] = intensities[internalGrid.getDensity()][k];
+ iExt[1][i][k] = derivatives[internalGrid.getDensity()][k];
+ }
- }
+ }
- return iExt;
- }
+ return iExt;
+ }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/IterativeSolver.java b/src/main/java/pulse/problem/schemes/rte/dom/IterativeSolver.java
index 86a1f672..6bfb45c1 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/IterativeSolver.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/IterativeSolver.java
@@ -6,104 +6,106 @@
import static pulse.properties.NumericPropertyKeyword.DOM_ITERATION_ERROR;
import static pulse.properties.NumericPropertyKeyword.RTE_MAX_ITERATIONS;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Set;
import pulse.problem.schemes.rte.RTECalculationStatus;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
-import pulse.properties.Property;
import pulse.util.PropertyHolder;
import pulse.util.Reflexive;
/**
- * Used to iteratively solve the radiative transfer problem. This is necessary since the latter can
- * only be solved separately for rays travelling in the positive or negative hemispheres. The problem
- * lies in the fact that the intensities of incoming and outward rays are coupled. The only way to
- * solve the coupled set of two Cauchy problems is iterative in nature.
- * This abstract class only defines the basic functionaly, its implementation is defined by the
- * subclasses.
+ * Used to iteratively solve the radiative transfer problem.
+ *
+ * This is necessary since the latter can only be solved separately for rays
+ * travelling in the positive or negative hemispheres. The problem lies in the
+ * fact that the intensities of incoming and outward rays are coupled. The only
+ * way to solve the coupled set of two Cauchy problems is iterative in
+ * nature.
+ *
+ * This abstract class only defines the basic functionaly, its implementation is
+ * defined by the subclasses.
*
*/
-
public abstract class IterativeSolver extends PropertyHolder implements Reflexive {
- private double iterationError;
- private int maxIterations;
-
- /**
- * Constructs an {@code IterativeSolver} with the default thresholds for
- * iteration error and number of iterations.
- */
-
- public IterativeSolver() {
- iterationError = (double) def(DOM_ITERATION_ERROR).getValue();
- maxIterations = (int) def(RTE_MAX_ITERATIONS).getValue();
- }
-
- /**
- * De-facto solves the radiative transfer problem iteratively.
- * @param integrator the integerator embedded in the iterative approach
- * @return a calculation status
- */
-
- public abstract RTECalculationStatus doIterations(AdaptiveIntegrator integrator);
-
- protected RTECalculationStatus sanityCheck(RTECalculationStatus status, int iterations) {
- return iterations < maxIterations ? status : ITERATION_LIMIT_REACHED;
- }
-
- public NumericProperty getIterationErrorTolerance() {
- return derive(DOM_ITERATION_ERROR, this.iterationError);
- }
-
- public void setIterationErrorTolerance(NumericProperty e) {
- if (e.getType() != DOM_ITERATION_ERROR)
- throw new IllegalArgumentException("Illegal type: " + e.getType());
- this.iterationError = (double) e.getValue();
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- switch (type) {
- case DOM_ITERATION_ERROR:
- setIterationErrorTolerance(property);
- break;
- case RTE_MAX_ITERATIONS:
- setMaxIterations(property);
- break;
- default:
- return;
- }
-
- firePropertyChanged(this, property);
-
- }
-
- @Override
- public List listedTypes() {
- List list = new ArrayList<>();
- list.add(def(DOM_ITERATION_ERROR));
- list.add(def(RTE_MAX_ITERATIONS));
- return list;
- }
-
- public NumericProperty getMaxIterations() {
- return derive(RTE_MAX_ITERATIONS, maxIterations);
- }
-
- public void setMaxIterations(NumericProperty iterations) {
- if (iterations.getType() == RTE_MAX_ITERATIONS)
- this.maxIterations = (int) iterations.getValue();
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + " : " + this.getIterationErrorTolerance();
- }
-
- public double getIterationError() {
- return iterationError;
- }
-
-}
\ No newline at end of file
+ private double iterationError;
+ private int maxIterations;
+
+ /**
+ * Constructs an {@code IterativeSolver} with the default thresholds for
+ * iteration error and number of iterations.
+ */
+ public IterativeSolver() {
+ iterationError = (double) def(DOM_ITERATION_ERROR).getValue();
+ maxIterations = (int) def(RTE_MAX_ITERATIONS).getValue();
+ }
+
+ /**
+ * De-facto solves the radiative transfer problem iteratively.
+ *
+ * @param integrator the integerator embedded in the iterative approach
+ * @return a calculation status
+ */
+ public abstract RTECalculationStatus doIterations(AdaptiveIntegrator integrator);
+
+ protected RTECalculationStatus sanityCheck(RTECalculationStatus status, int iterations) {
+ return iterations < maxIterations ? status : ITERATION_LIMIT_REACHED;
+ }
+
+ public NumericProperty getIterationErrorTolerance() {
+ return derive(DOM_ITERATION_ERROR, this.iterationError);
+ }
+
+ public void setIterationErrorTolerance(NumericProperty e) {
+ if (e.getType() != DOM_ITERATION_ERROR) {
+ throw new IllegalArgumentException("Illegal type: " + e.getType());
+ }
+ this.iterationError = (double) e.getValue();
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ switch (type) {
+ case DOM_ITERATION_ERROR:
+ setIterationErrorTolerance(property);
+ break;
+ case RTE_MAX_ITERATIONS:
+ setMaxIterations(property);
+ break;
+ default:
+ return;
+ }
+
+ firePropertyChanged(this, property);
+
+ }
+
+ @Override
+ public Set listedKeywords() {
+ var set = super.listedKeywords();
+ set.add(DOM_ITERATION_ERROR);
+ set.add(RTE_MAX_ITERATIONS);
+ return set;
+ }
+
+ public NumericProperty getMaxIterations() {
+ return derive(RTE_MAX_ITERATIONS, maxIterations);
+ }
+
+ public void setMaxIterations(NumericProperty iterations) {
+ if (iterations.getType() == RTE_MAX_ITERATIONS) {
+ this.maxIterations = (int) iterations.getValue();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " : " + this.getIterationErrorTolerance();
+ }
+
+ public double getIterationError() {
+ return iterationError;
+ }
+
+}
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 dbecad8d..4693eeb4 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/LinearAnisotropicPF.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/LinearAnisotropicPF.java
@@ -1,36 +1,36 @@
package pulse.problem.schemes.rte.dom;
import pulse.problem.statements.ParticipatingMedium;
+import pulse.problem.statements.model.ThermoOpticalProperties;
/**
* The linear-anisotropic scattering phase function.
*
*/
-
public class LinearAnisotropicPF extends PhaseFunction {
- private double g;
-
- public LinearAnisotropicPF(ParticipatingMedium medium, Discretisation intensities) {
- super(medium, intensities);
- }
-
- @Override
- public void init(ParticipatingMedium medium) {
- super.init(medium);
- g = 3.0 * getAnisotropyFactor();
- }
-
- @Override
- public double partialSum(final int i, final int j, final int n1, final int n2Exclusive) {
- final var intensities = getDiscreteIntensities();
- return intensities.incidentRadiation(j, n1, n2Exclusive) + g * intensities.getOrdinates().getNode(i) * intensities.flux(j, n1, n2Exclusive);
- }
-
- @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);
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = 7074989018933263351L;
+ private double g;
+
+ public LinearAnisotropicPF(ThermoOpticalProperties top, Discretisation intensities) {
+ super(top, intensities);
+ }
+
+ @Override
+ public void init(ThermoOpticalProperties top) {
+ super.init(top);
+ g = 3.0 * getAnisotropyFactor();
+ }
+
+ @Override
+ public double partialSum(final int i, final int j, final int n1, final int n2Exclusive) {
+ final var intensities = getDiscreteIntensities();
+ return intensities.incidentRadiation(j, n1, n2Exclusive) + g * intensities.getOrdinates().getNode(i) * intensities.flux(j, n1, n2Exclusive);
+ }
+
+ @Override
+ public double function(final int i, final int 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 21eda5c0..3dc388f9 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/ODEIntegrator.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/ODEIntegrator.java
@@ -2,137 +2,140 @@
import pulse.problem.schemes.rte.BlackbodySpectrum;
import pulse.problem.schemes.rte.RTECalculationStatus;
-import pulse.problem.statements.ParticipatingMedium;
-import pulse.problem.statements.ThermoOpticalProperties;
+import pulse.problem.statements.NonlinearProblem;
+import pulse.problem.statements.model.ThermoOpticalProperties;
import pulse.util.PropertyHolder;
import pulse.util.Reflexive;
public abstract class ODEIntegrator extends PropertyHolder implements Reflexive {
- private Discretisation discretisation;
- private PhaseFunction pf;
- private BlackbodySpectrum spectrum;
+ private Discretisation discretisation;
+ private PhaseFunction pf;
+ private BlackbodySpectrum spectrum;
- public ODEIntegrator(Discretisation intensities) {
- setDiscretisation(intensities);
- }
+ public ODEIntegrator(Discretisation intensities) {
+ setDiscretisation(intensities);
+ }
- public abstract RTECalculationStatus integrate();
+ public abstract RTECalculationStatus integrate();
- protected void init(ParticipatingMedium problem) {
- discretisation.setEmissivity((double)problem.getProperties().getEmissivity().getValue());
- var properties = (ThermoOpticalProperties)problem.getProperties();
- discretisation.setGrid(new StretchedGrid((double) properties.getOpticalThickness().getValue()));
- setEmissionFunction(new BlackbodySpectrum(problem));
- }
+ protected void init(NonlinearProblem problem) {
+ extract((ThermoOpticalProperties) problem.getProperties());
+ setEmissionFunction(new BlackbodySpectrum(problem));
+ }
- protected void treatZeroIndex() {
- var ordinates = discretisation.getOrdinates();
+ protected void extract(ThermoOpticalProperties properties) {
+ discretisation.setEmissivity((double) properties.getEmissivity().getValue());
+ discretisation.setGrid(new StretchedGrid((double) properties.getOpticalThickness().getValue()));
+ }
- if (ordinates.hasZeroNode()) {
+ protected void treatZeroIndex() {
+ var ordinates = discretisation.getOrdinates();
- var grid = discretisation.getGrid();
- var quantities = discretisation.getQuantities();
- double denominator = 0;
- final double halfAlbedo = pf.getHalfAlbedo();
+ if (ordinates.hasZeroNode()) {
- // loop through the spatial indices
- for (int j = 0; j < grid.getDensity() + 1; j++) {
+ var grid = discretisation.getGrid();
+ var quantities = discretisation.getQuantities();
+ double denominator = 0;
+ final double halfAlbedo = pf.getHalfAlbedo();
- // solve I_k = S_k for mu[k] = 0
- denominator = 1.0 - halfAlbedo * ordinates.getWeight(0) * pf.function(0, 0);
- quantities.setIntensity(j, 0,
- (emission(grid.getNode(j)) + halfAlbedo * pf.sumExcludingIndex(0, j, 0)) / denominator);
+ // loop through the spatial indices
+ for (int j = 0; j < grid.getDensity() + 1; j++) {
- }
- }
+ // solve I_k = S_k for mu[k] = 0
+ denominator = 1.0 - halfAlbedo * ordinates.getWeight(0) * pf.function(0, 0);
+ quantities.setIntensity(j, 0,
+ (emission(grid.getNode(j)) + halfAlbedo * pf.sumExcludingIndex(0, j, 0)) / denominator);
- }
+ }
+ }
- public double derivative(int i, int j, double t, double I) {
- return 1.0 / discretisation.getOrdinates().getNode(i) * (source(i, j, t, I) - I);
- }
+ }
- public double derivative(int i, double t, double[] out, double[] in, int l1, int l2) {
- return 1.0 / discretisation.getOrdinates().getNode(i) * (source(i, out, in, t, l1, l2) - out[i - l1]);
- }
+ public double derivative(int i, int j, double t, double I) {
+ return 1.0 / discretisation.getOrdinates().getNode(i) * (source(i, j, t, I) - I);
+ }
- public double partial(int i, double t, double[] inward, int l1, int l2) {
- return (emission(t) + pf.getHalfAlbedo() * pf.inwardPartialSum(i, inward, l1, l2))
- / discretisation.getOrdinates().getNode(i);
- }
+ public double derivative(int i, double t, double[] out, double[] in, int l1, int l2) {
+ return 1.0 / discretisation.getOrdinates().getNode(i) * (source(i, out, in, t, l1, l2) - out[i - l1]);
+ }
- public double partial(int i, int j, double t, int l1, int l2) {
- return (emission(t) + pf.getHalfAlbedo() * pf.partialSum(i, j, l1, l2))
- / discretisation.getOrdinates().getNode(i);
- }
+ public double partial(int i, double t, double[] inward, int l1, int l2) {
+ return (emission(t) + pf.getHalfAlbedo() * pf.inwardPartialSum(i, inward, l1, l2))
+ / discretisation.getOrdinates().getNode(i);
+ }
- public double source(int i, int j, double t, double I) {
- return emission(t) + pf.getHalfAlbedo()
- * (pf.sumExcludingIndex(i, j, i) + pf.function(i, i) * discretisation.getOrdinates().getWeight(i) * I);
- }
+ public double partial(int i, int j, double t, int l1, int l2) {
+ return (emission(t) + pf.getHalfAlbedo() * pf.partialSum(i, j, l1, l2))
+ / discretisation.getOrdinates().getNode(i);
+ }
- public double source(final int i, final double[] iOut, final double[] iIn, final double t, final int l1,
- final int l2) {
+ public double source(int i, int j, double t, double I) {
+ return emission(t) + pf.getHalfAlbedo()
+ * (pf.sumExcludingIndex(i, j, i) + pf.function(i, i) * discretisation.getOrdinates().getWeight(i) * I);
+ }
- double sumOut = 0;
- final var ordinates = discretisation.getOrdinates();
+ public double source(final int i, final double[] iOut, final double[] iIn, final double t, final int l1,
+ final int l2) {
- for (int l = l1; l < l2; l++) {
- // sum over the OUTWARD intensities iOut
- sumOut += iOut[l - l1] * ordinates.getWeight(l) * pf.function(i, l);
- }
+ double sumOut = 0;
+ final var ordinates = discretisation.getOrdinates();
- double sumIn = 0;
+ for (int l = l1; l < l2; l++) {
+ // sum over the OUTWARD intensities iOut
+ sumOut += iOut[l - l1] * ordinates.getWeight(l) * pf.function(i, l);
+ }
- for (int start = ordinates.getTotalNodes() - l2, l = start, end = ordinates.getTotalNodes()
- - l1; l < end; l++) {
- // sum over the INWARD
- // intensities iIn
- sumIn += iIn[l - start] * ordinates.getWeight(l) * pf.function(i, l);
- }
+ double sumIn = 0;
- return emission(t) + pf.getHalfAlbedo() * (sumIn + sumOut); // contains sum over the incoming rays
+ for (int start = ordinates.getTotalNodes() - l2, l = start, end = ordinates.getTotalNodes()
+ - l1; l < end; l++) {
+ // sum over the INWARD
+ // intensities iIn
+ sumIn += iIn[l - start] * ordinates.getWeight(l) * pf.function(i, l);
+ }
- }
+ return emission(t) + pf.getHalfAlbedo() * (sumIn + sumOut); // contains sum over the incoming rays
- public double emission(double t) {
- return (1.0 - 2.0 * pf.getHalfAlbedo()) * spectrum.radianceAt(t);
- }
+ }
- public PhaseFunction getPhaseFunction() {
- return pf;
- }
+ public double emission(double t) {
+ return (1.0 - 2.0 * pf.getHalfAlbedo()) * spectrum.radianceAt(t);
+ }
- protected void setPhaseFunction(PhaseFunction pf) {
- this.pf = pf;
- }
+ public final PhaseFunction getPhaseFunction() {
+ return pf;
+ }
- @Override
- public String getDescriptor() {
- return "Numeric integrator";
- }
+ protected final void setPhaseFunction(PhaseFunction pf) {
+ this.pf = pf;
+ }
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
+ @Override
+ public String getDescriptor() {
+ return "Numeric integrator";
+ }
- public Discretisation getDiscretisation() {
- return discretisation;
- }
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
- public void setDiscretisation(Discretisation discretisation) {
- this.discretisation = discretisation;
- discretisation.setParent(this);
- }
+ public final Discretisation getDiscretisation() {
+ return discretisation;
+ }
- public BlackbodySpectrum getEmissionFunction() {
- return spectrum;
- }
+ public final void setDiscretisation(Discretisation discretisation) {
+ this.discretisation = discretisation;
+ discretisation.setParent(this);
+ }
- public void setEmissionFunction(BlackbodySpectrum emissionFunction) {
- this.spectrum = emissionFunction;
- }
+ public final BlackbodySpectrum getEmissionFunction() {
+ return spectrum;
+ }
-}
\ No newline at end of file
+ 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 60734ee6..fc09890d 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/OrdinateSet.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/OrdinateSet.java
@@ -1,5 +1,6 @@
package pulse.problem.schemes.rte.dom;
+import java.io.Serializable;
import static pulse.math.MathUtils.approximatelyEquals;
import java.util.Arrays;
@@ -7,107 +8,109 @@
import pulse.util.Descriptive;
/**
- * A fixed set of discrete cosine nodes and weights for the angular discretisation of a radiative
- * transfer equation.
+ * A fixed set of discrete cosine nodes and weights for the angular
+ * discretisation of a radiative transfer equation.
*
*/
+public class OrdinateSet implements Descriptive, Serializable {
-public class OrdinateSet implements Descriptive {
+ private static final long serialVersionUID = 4850346144315192409L;
+ private double[] mu;
+ private double[] w;
- private double[] mu;
- private double[] w;
+ private int firstPositiveNode;
+ private int firstNegativeNode;
+ private int totalNodes;
- private int firstPositiveNode;
- private int firstNegativeNode;
- private int totalNodes;
+ public final static String DEFAULT_SET = "G8M";
+ private String name;
- public final static String DEFAULT_SET = "G8M";
- private String name;
+ public OrdinateSet(String name, double[] mu, double[] w) {
+ if (mu.length != w.length) {
+ throw new IllegalArgumentException("Arrays sizes do not match: " + mu.length + " != " + w.length);
+ }
- public OrdinateSet(String name, double[] mu, double[] w) {
- if (mu.length != w.length)
- throw new IllegalArgumentException("Arrays sizes do not match: " + mu.length + " != " + w.length);
+ setName(name);
+ this.mu = mu;
+ this.w = w;
+ totalNodes = mu.length;
- setName(name);
- this.mu = mu;
- this.w = w;
- totalNodes = mu.length;
+ checkWeights();
- checkWeights();
+ firstPositiveNode = hasZeroNode() ? 1 : 0; //zero node should always be the first one
+ firstNegativeNode = totalNodes / 2 + (hasZeroNode() ? 1 : 0);
- firstPositiveNode = hasZeroNode() ? 1 : 0; //zero node should always be the first one
- firstNegativeNode = totalNodes / 2 + (hasZeroNode() ? 1 : 0);
-
- }
+ }
- @Override
- public String toString() {
- return this.getName();
- }
+ @Override
+ public String toString() {
+ return this.getName();
+ }
- public String printOrdinateSet() {
- var sb = new StringBuilder();
+ public String printOrdinateSet() {
+ var sb = new StringBuilder();
- sb.append("Quadrature set: " + this.getName());
- sb.append(System.lineSeparator());
+ sb.append("Quadrature set: " + this.getName());
+ sb.append(System.lineSeparator());
- for (int i = 0; i < mu.length; i++) {
- sb.append(String.format("%nmu[%1d] = %3.8f; w[%1d] = %3.8f", i, mu[i], i, w[i]));
- }
+ for (int i = 0; i < mu.length; i++) {
+ sb.append(String.format("%nmu[%1d] = %3.8f; w[%1d] = %3.8f", i, mu[i], i, w[i]));
+ }
- return sb.toString();
+ return sb.toString();
- }
+ }
- public boolean hasZeroNode() {
- return Arrays.stream(mu).anyMatch(Double.valueOf(0.0)::equals);
- }
+ public final boolean hasZeroNode() {
+ return Arrays.stream(mu).anyMatch(Double.valueOf(0.0)::equals);
+ }
- public int getFirstPositiveNode() {
- return firstPositiveNode;
- }
+ public int getFirstPositiveNode() {
+ return firstPositiveNode;
+ }
- public int getFirstNegativeNode() {
- return firstNegativeNode;
- }
+ public int getFirstNegativeNode() {
+ return firstNegativeNode;
+ }
- public String getName() {
- return name;
- }
+ public String getName() {
+ return name;
+ }
- public void setName(String name) {
- this.name = name;
- }
+ public final void setName(String name) {
+ this.name = name;
+ }
- @Override
- public String describe() {
- return "Ordinate set";
- }
+ @Override
+ public String describe() {
+ return "Ordinate set";
+ }
- public int getNumberOfNodes() {
- return totalNodes;
- }
+ public int getNumberOfNodes() {
+ return totalNodes;
+ }
- public int getTotalNodes() {
- return totalNodes;
- }
+ public int getTotalNodes() {
+ return totalNodes;
+ }
- public double getNode(int i) {
- return mu[i];
- }
+ public double getNode(int i) {
+ return mu[i];
+ }
- public double getWeight(int i) {
- return w[i];
- }
+ public double getWeight(int i) {
+ return w[i];
+ }
- public int getHalfLength() {
- return firstNegativeNode - firstPositiveNode;
- }
+ public int getHalfLength() {
+ return firstNegativeNode - firstPositiveNode;
+ }
- private void checkWeights() {
- final double sum = Arrays.stream(w).sum();
- if (!approximatelyEquals(sum, 2.0))
- throw new IllegalStateException("Summed quadrature weights != 2.0");
- }
-
-}
\ No newline at end of file
+ private void checkWeights() {
+ final double sum = Arrays.stream(w).sum();
+ if (!approximatelyEquals(sum, 2.0)) {
+ throw new IllegalStateException("Summed quadrature weights != 2.0");
+ }
+ }
+
+}
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 addb751b..21374fb6 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/PhaseFunction.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/PhaseFunction.java
@@ -1,73 +1,85 @@
package pulse.problem.schemes.rte.dom;
-import pulse.problem.statements.ParticipatingMedium;
-import pulse.problem.statements.ThermoOpticalProperties;
+import java.io.Serializable;
+import pulse.problem.statements.model.ThermoOpticalProperties;
import pulse.util.Reflexive;
-public abstract class PhaseFunction implements Reflexive {
-
- private Discretisation intensities;
- private double anisotropy;
- private double halfAlbedo;
-
- public PhaseFunction(ParticipatingMedium medium, Discretisation intensities) {
- this.intensities = intensities;
- init(medium);
- }
-
- public double fullSum(int i, int j) {
- return partialSum(i, j, 0, intensities.getOrdinates().getTotalNodes());
- }
-
- public double sumExcludingIndex(int i, int j, int index) {
- return partialSum(i, j, 0, index) + partialSum(i, j, index + 1, intensities.getOrdinates().getTotalNodes());
- }
-
- public double partialSum(int i, int j, int startInclusive, int endExclusive) {
- double result = 0;
- final var ordinates = intensities.getOrdinates();
- final var quantities = intensities.getQuantities();
-
- for (int k = startInclusive; k < endExclusive; k++) {
- result += ordinates.getWeight(k) * quantities.getIntensity(j, k) * function(i, k);
- }
- return result;
- }
-
- public double inwardPartialSum(int i, double[] inward, int kStart, int kEndExclusive) {
- double result = 0;
- final var ordinates = intensities.getOrdinates();
-
- for (int k = kStart; k < kEndExclusive; k++) {
- result += ordinates.getWeight(k) * inward[k - kStart] * function(i, k);
- }
-
- return result;
- }
-
- public abstract double function(int i, int k);
-
- public double getAnisotropyFactor() {
- return anisotropy;
- }
-
- protected Discretisation getDiscreteIntensities() {
- return intensities;
- }
-
- public void init(ParticipatingMedium problem) {
- var properties = (ThermoOpticalProperties)problem.getProperties();
- this.anisotropy = (double) properties.getScatteringAnisostropy().getValue();
- this.halfAlbedo = 0.5 * (double) properties.getScatteringAlbedo().getValue();
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName();
- }
-
- public double getHalfAlbedo() {
- return halfAlbedo;
- }
-
-}
\ No newline at end of file
+public abstract class PhaseFunction implements Reflexive, Serializable {
+
+ private final Discretisation intensities;
+ private double anisotropy;
+ private double halfAlbedo;
+
+ public PhaseFunction(ThermoOpticalProperties top, Discretisation intensities) {
+ this.intensities = intensities;
+ init(top);
+ }
+
+ /**
+ * Calculates the cosine of the scattering angle as the product of the two
+ * discrete cosine nodes.
+ *
+ * @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) {
+ return partialSum(i, j, 0, intensities.getOrdinates().getTotalNodes());
+ }
+
+ public double sumExcludingIndex(int i, int j, int index) {
+ return partialSum(i, j, 0, index) + partialSum(i, j, index + 1, intensities.getOrdinates().getTotalNodes());
+ }
+
+ public double partialSum(int i, int j, int startInclusive, int endExclusive) {
+ double result = 0;
+ final var ordinates = intensities.getOrdinates();
+ final var quantities = intensities.getQuantities();
+
+ for (int k = startInclusive; k < endExclusive; k++) {
+ result += ordinates.getWeight(k) * quantities.getIntensity(j, k) * function(i, k);
+ }
+ return result;
+ }
+
+ public double inwardPartialSum(int i, double[] inward, int kStart, int kEndExclusive) {
+ double result = 0;
+ final var ordinates = intensities.getOrdinates();
+
+ for (int k = kStart; k < kEndExclusive; k++) {
+ result += ordinates.getWeight(k) * inward[k - kStart] * function(i, k);
+ }
+
+ return result;
+ }
+
+ public abstract double function(int i, int k);
+
+ public double getAnisotropyFactor() {
+ return anisotropy;
+ }
+
+ protected Discretisation getDiscreteIntensities() {
+ return intensities;
+ }
+
+ public void init(ThermoOpticalProperties properties) {
+ this.anisotropy = (double) properties.getScatteringAnisostropy().getValue();
+ this.halfAlbedo = 0.5 * (double) properties.getScatteringAlbedo().getValue();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+
+ public double getHalfAlbedo() {
+ return halfAlbedo;
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/StretchedGrid.java b/src/main/java/pulse/problem/schemes/rte/dom/StretchedGrid.java
index 7a26d5f5..012eb5d3 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/StretchedGrid.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/StretchedGrid.java
@@ -5,147 +5,150 @@
import static pulse.properties.NumericPropertyKeyword.DOM_GRID_DENSITY;
import static pulse.properties.NumericPropertyKeyword.GRID_STRETCHING_FACTOR;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Set;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
-import pulse.properties.Property;
import pulse.util.PropertyHolder;
public class StretchedGrid extends PropertyHolder {
- private double[] nodes;
-
- private double stretchingFactor;
- private double dimension;
-
- /**
- * Constructs a uniform grid where the dimension is set to the argument. The stretching factor
- * and grid density are set to a default value.
- * @param dimension the dimension of the grid
- */
-
- public StretchedGrid(double dimension) {
- this(def(DOM_GRID_DENSITY), dimension, def(GRID_STRETCHING_FACTOR), true);
- }
-
- /**
- * Constructs a non-uniform grid where the dimension and the grid density are specified by the arguments. The stretching factor
- * and grid density are set to a default value.
- * @param gridDensity the grid density, which is a property of the {@code DOM_GRID_DENSITY} type
- * @param dimension the dimension of the grid
- */
-
- public StretchedGrid(NumericProperty gridDensity, double dimension) {
- this(gridDensity, dimension, def(GRID_STRETCHING_FACTOR), false);
- }
-
- protected StretchedGrid(NumericProperty gridDensity, double dimension, NumericProperty stretchingFactor, boolean uniform) {
- this.stretchingFactor = (double) stretchingFactor.getValue();
- this.dimension = dimension;
- int n = (int) gridDensity.getValue();
- if (uniform)
- generateUniformBase(n, true);
- else
- generate(n);
- }
-
- public void generate(int n) {
- generateUniformBase(n, false);
-
- // apply stretching function
-
- for (int i = 0; i < nodes.length; i++) {
- nodes[i] = 0.5 * dimension * tanh(nodes[i], stretchingFactor);
- }
-
- }
-
- public void generateUniform(boolean scaled) {
- int n1 = (int) def(DOM_GRID_DENSITY).getValue();
- generateUniformBase(n1, scaled);
- }
-
- public void generateUniformBase(int n, boolean scaled) {
- nodes = new double[n + 1];
- double h = (scaled ? dimension : 1.0) / n;
-
- for (int i = 0; i < nodes.length; i++) {
- nodes[i] = i * h;
- }
-
- }
-
- public int getDensity() {
- return nodes.length - 1;
- }
-
- public NumericProperty getStretchingFactor() {
- return derive(GRID_STRETCHING_FACTOR, stretchingFactor);
- }
-
- public void setStretchingFactor(NumericProperty p) {
- if (p.getType() != GRID_STRETCHING_FACTOR)
- throw new IllegalArgumentException("Illegal type: " + p.getType());
- this.stretchingFactor = (double) p.getValue();
- }
-
- public double getDimension() {
- return dimension;
- }
-
- public double getNode(int i) {
- return nodes[i];
- }
-
- public double[] getNodes() {
- return nodes;
- }
-
- public double step(int i, double sign) {
- return nodes[i + (int) ((1. + sign) * 0.5)] - nodes[i - (int) ((1. - sign) * 0.5)];
- }
-
- public double stepLeft(int i) {
- return nodes[i] - nodes[i - 1];
- }
-
- public double stepRight(int i) {
- return nodes[i + 1] - nodes[i];
- }
-
- public double tanh(final double x, final double stretchingFactor) {
- return 1.0 - Math.tanh(stretchingFactor * (1.0 - 2.0 * x)) / Math.tanh(stretchingFactor);
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- switch (type) {
- case GRID_STRETCHING_FACTOR:
- setStretchingFactor(property);
- break;
- default:
- throw new IllegalArgumentException("Unknown type: " + type);
- }
- }
-
- @Override
- public List listedTypes() {
- List list = new ArrayList<>();
- list.add(def(GRID_STRETCHING_FACTOR));
- list.add(def(DOM_GRID_DENSITY));
- return list;
- }
-
- @Override
- public String toString() {
- return "{ " + derive(DOM_GRID_DENSITY, getDensity()) + " ; " + getStretchingFactor() + " }";
- }
-
- @Override
- public String getDescriptor() {
- return "Adaptive grid";
- }
-
-}
\ No newline at end of file
+ private static final long serialVersionUID = -7987714138817824037L;
+
+ private double[] nodes;
+
+ private double stretchingFactor;
+ private double dimension;
+
+ /**
+ * Constructs a uniform grid where the dimension is set to the argument. The
+ * stretching factor and grid density are set to a default value.
+ *
+ * @param dimension the dimension of the grid
+ */
+ public StretchedGrid(double dimension) {
+ this(def(DOM_GRID_DENSITY), dimension, def(GRID_STRETCHING_FACTOR), true);
+ }
+
+ /**
+ * Constructs a non-uniform grid where the dimension and the grid density
+ * are specified by the arguments. The stretching factor and grid density
+ * are set to a default value.
+ *
+ * @param gridDensity the grid density, which is a property of the
+ * {@code DOM_GRID_DENSITY} type
+ * @param dimension the dimension of the grid
+ */
+ public StretchedGrid(NumericProperty gridDensity, double dimension) {
+ this(gridDensity, dimension, def(GRID_STRETCHING_FACTOR), false);
+ }
+
+ protected StretchedGrid(NumericProperty gridDensity, double dimension, NumericProperty stretchingFactor, boolean uniform) {
+ this.stretchingFactor = (double) stretchingFactor.getValue();
+ this.dimension = dimension;
+ int n = (int) gridDensity.getValue();
+ if (uniform) {
+ generateUniformBase(n, true);
+ } else {
+ generate(n);
+ }
+ }
+
+ public void generate(int n) {
+ generateUniformBase(n, false);
+
+ // apply stretching function
+ for (int i = 0; i < nodes.length; i++) {
+ nodes[i] = 0.5 * dimension * tanh(nodes[i], stretchingFactor);
+ }
+
+ }
+
+ public void generateUniform(boolean scaled) {
+ int n1 = (int) def(DOM_GRID_DENSITY).getValue();
+ generateUniformBase(n1, scaled);
+ }
+
+ public void generateUniformBase(int n, boolean scaled) {
+ nodes = new double[n + 1];
+ double h = (scaled ? dimension : 1.0) / n;
+
+ for (int i = 0; i < nodes.length; i++) {
+ nodes[i] = i * h;
+ }
+
+ }
+
+ public int getDensity() {
+ return nodes.length - 1;
+ }
+
+ public NumericProperty getStretchingFactor() {
+ return derive(GRID_STRETCHING_FACTOR, stretchingFactor);
+ }
+
+ public void setStretchingFactor(NumericProperty p) {
+ if (p.getType() != GRID_STRETCHING_FACTOR) {
+ throw new IllegalArgumentException("Illegal type: " + p.getType());
+ }
+ this.stretchingFactor = (double) p.getValue();
+ }
+
+ public double getDimension() {
+ return dimension;
+ }
+
+ public double getNode(int i) {
+ return nodes[i];
+ }
+
+ public double[] getNodes() {
+ return nodes;
+ }
+
+ public double step(int i, double sign) {
+ return nodes[i + (int) ((1. + sign) * 0.5)] - nodes[i - (int) ((1. - sign) * 0.5)];
+ }
+
+ public double stepLeft(int i) {
+ return nodes[i] - nodes[i - 1];
+ }
+
+ public double stepRight(int i) {
+ return nodes[i + 1] - nodes[i];
+ }
+
+ public double tanh(final double x, final double stretchingFactor) {
+ return 1.0 - Math.tanh(stretchingFactor * (1.0 - 2.0 * x)) / Math.tanh(stretchingFactor);
+ }
+
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
+ switch (type) {
+ case GRID_STRETCHING_FACTOR:
+ setStretchingFactor(property);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ }
+
+ @Override
+ public Set listedKeywords() {
+ var set = super.listedKeywords();
+ set.add(GRID_STRETCHING_FACTOR);
+ set.add(DOM_GRID_DENSITY);
+ return set;
+ }
+
+ @Override
+ public String toString() {
+ return "{ " + derive(DOM_GRID_DENSITY, getDensity()) + " ; " + getStretchingFactor() + " }";
+ }
+
+ @Override
+ public String getDescriptor() {
+ return "Adaptive grid";
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/SuccessiveOverrelaxation.java b/src/main/java/pulse/problem/schemes/rte/dom/SuccessiveOverrelaxation.java
index 691ea493..1c44c065 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/SuccessiveOverrelaxation.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/SuccessiveOverrelaxation.java
@@ -5,118 +5,120 @@
import static pulse.properties.NumericProperties.derive;
import static pulse.properties.NumericPropertyKeyword.RELAXATION_PARAMETER;
-import java.util.List;
+import java.util.Set;
import pulse.problem.schemes.rte.RTECalculationStatus;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
-import pulse.properties.Property;
public class SuccessiveOverrelaxation extends IterativeSolver {
- private double W;
+ private static final long serialVersionUID = 1135563981945852881L;
+ private double W;
- public SuccessiveOverrelaxation() {
- super();
- this.W = (double) def(RELAXATION_PARAMETER).getValue();
- }
+ public SuccessiveOverrelaxation() {
+ super();
+ this.W = (double) def(RELAXATION_PARAMETER).getValue();
+ }
- private void successiveOverrelaxation(AdaptiveIntegrator integrator) {
+ private void successiveOverrelaxation(AdaptiveIntegrator integrator) {
- final var intensities = integrator.getDiscretisation();
- final var quantities = intensities.getQuantities();
- final int density = intensities.getGrid().getDensity();
- final int total = intensities.getOrdinates().getTotalNodes();
+ final var intensities = integrator.getDiscretisation();
+ final var quantities = intensities.getQuantities();
+ final int density = intensities.getGrid().getDensity();
+ final int total = intensities.getOrdinates().getTotalNodes();
- final double ONE_MINUS_W = 1.0 - W;
+ final double ONE_MINUS_W = 1.0 - W;
- for (int i = 0; i < density + 1; i++) {
- for (int j = 0; j < total; j++) {
- quantities.setIntensity(i, j,
- ONE_MINUS_W * quantities.getStoredIntensity(i, j) + W * quantities.getIntensity(i, j));
- quantities.setStoredDerivative(i, j,
- ONE_MINUS_W * quantities.getStoredDerivative(i, j) + W * quantities.getDerivative(i, j));
- }
- }
+ for (int i = 0; i < density + 1; i++) {
+ for (int j = 0; j < total; j++) {
+ quantities.setIntensity(i, j,
+ ONE_MINUS_W * quantities.getStoredIntensity(i, j) + W * quantities.getIntensity(i, j));
+ quantities.setStoredDerivative(i, j,
+ ONE_MINUS_W * quantities.getStoredDerivative(i, j) + W * quantities.getDerivative(i, j));
+ }
+ }
- }
+ }
- @Override
- public RTECalculationStatus doIterations(AdaptiveIntegrator integrator) {
+ @Override
+ public RTECalculationStatus doIterations(AdaptiveIntegrator integrator) {
- var discrete = integrator.getDiscretisation();
- var quantities = discrete.getQuantities();
- double relativeError = 100;
+ var discrete = integrator.getDiscretisation();
+ var quantities = discrete.getQuantities();
+ double relativeError = 100;
- double qld = 0;
- double qrd = 0;
- double qsum;
+ double qld = 0;
+ double qrd = 0;
+ double qsum;
- int iterations = 0;
- final var ef = integrator.getEmissionFunction();
- RTECalculationStatus status = RTECalculationStatus.NORMAL;
+ int iterations = 0;
+ final var ef = integrator.getEmissionFunction();
+ RTECalculationStatus status = RTECalculationStatus.NORMAL;
- for (double ql = 1e8, qr = ql; relativeError > getIterationError(); status = sanityCheck(status,
- ++iterations)) {
- quantities.store();
- ql = qld;
- qr = qrd;
-
- status = integrator.integrate();
-
- // if the integrator attempted rescaling, last iteration is not valid anymore
- if (integrator.wasRescaled()) {
- relativeError = Double.POSITIVE_INFINITY;
- } else { // calculate the (k+1) iteration as: I_k+1 = I_k * (1 - W) + I*
+ for (double ql = 1e8, qr = ql; relativeError > getIterationError(); status = sanityCheck(status,
+ ++iterations)) {
+ quantities.store();
+ ql = qld;
+ qr = qrd;
- // get the difference in boundary heat fluxes
- qld = discrete.fluxLeft(ef);
- qrd = discrete.fluxRight(ef);
- qsum = abs(qld - ql) + abs(qrd - qr);
+ status = integrator.integrate();
- successiveOverrelaxation(integrator);
- relativeError = qsum / ( abs(qld) + abs(qrd) );
-
- }
+ // if the integrator attempted rescaling, last iteration is not valid anymore
+ if (integrator.wasRescaled()) {
+ relativeError = Double.POSITIVE_INFINITY;
+ } else { // calculate the (k+1) iteration as: I_k+1 = I_k * (1 - W) + I*
- }
+ // get the difference in boundary heat fluxes
+ qld = discrete.fluxLeft(ef);
+ qrd = discrete.fluxRight(ef);
+ qsum = abs(qld - ql) + abs(qrd - qr);
- return status;
+ successiveOverrelaxation(integrator);
+ relativeError = qsum / (abs(qld) + abs(qrd));
- }
+ }
- public NumericProperty getRelaxationParameter() {
- return derive(RELAXATION_PARAMETER, W);
- }
+ }
- public void setRelaxationParameter(NumericProperty p) {
- if (p.getType() != RELAXATION_PARAMETER)
- throw new IllegalArgumentException("Unknown type: " + p.getType());
- W = (double) p.getValue();
- }
+ return status;
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
+ }
- super.set(type, property);
+ public NumericProperty getRelaxationParameter() {
+ return derive(RELAXATION_PARAMETER, W);
+ }
- if (type == RELAXATION_PARAMETER)
- setRelaxationParameter(property);
- else
- throw new IllegalArgumentException("Unknown type: " + type);
+ public void setRelaxationParameter(NumericProperty p) {
+ if (p.getType() != RELAXATION_PARAMETER) {
+ throw new IllegalArgumentException("Unknown type: " + p.getType());
+ }
+ W = (double) p.getValue();
+ }
- }
+ @Override
+ public void set(NumericPropertyKeyword type, NumericProperty property) {
- @Override
- public List listedTypes() {
- List list = super.listedTypes();
- list.add(def(RELAXATION_PARAMETER));
- return list;
- }
+ super.set(type, property);
- @Override
- public String toString() {
- return super.toString() + " ; " + getRelaxationParameter();
- }
-
-}
\ No newline at end of file
+ if (type == RELAXATION_PARAMETER) {
+ setRelaxationParameter(property);
+ } else {
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+
+ }
+
+ @Override
+ public Set listedKeywords() {
+ var set = super.listedKeywords();
+ set.add(RELAXATION_PARAMETER);
+ return set;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " ; " + getRelaxationParameter();
+ }
+
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/TRBDF2.java b/src/main/java/pulse/problem/schemes/rte/dom/TRBDF2.java
index b84c5bfd..5ce22402 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/TRBDF2.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/TRBDF2.java
@@ -6,24 +6,24 @@
import pulse.problem.schemes.rte.RTECalculationStatus;
/**
- * TRBDF2 (Trapezoidal Backward Differencing Second Order) Scheme for the solution of one-dimensional radiative transfer problems.
- *
+ * TRBDF2 (Trapezoidal Backward Differencing Second Order) Scheme for the
+ * solution of one-dimensional radiative transfer problems.
+ *
* @author Artem Lunev, Vadim Zborovskii
*
*/
-
public class TRBDF2 extends AdaptiveIntegrator {
- /*
+ private static final long serialVersionUID = 5488454845395333565L;
+ /*
* Coefficients of the Butcher tableau as originally defined in M.E. Hosea, L.E
* Shampine/Applied Numerical Mathematics 20 (1996) 21-37
- */
+ */
+ private final static double gamma = 2.0 - Math.sqrt(2.0);
+ private final static double w = Math.sqrt(2.0) / 4.0;
+ private final static double d = gamma / 2.0;
- private final static double gamma = 2.0 - Math.sqrt(2.0);
- private final static double w = Math.sqrt(2.0) / 4.0;
- private final static double d = gamma / 2.0;
-
- /*
+ /*
* Uncomment this for coefficients for the error estimator from: Christopher A.
* Kennedy, Mark H. Carpenter. Diagonally Implicit Runge-Kutta Methods for
* Ordinary Differential Equations. A Review. NASA/TM–2016–219173, p. 72
@@ -33,209 +33,198 @@ public class TRBDF2 extends AdaptiveIntegrator {
* g) / (2.0 * g - 1.0); bHat[0] = 1.0 - bHat[1] - bHat[2];
*
*
- */
-
- private static double[] bHat = new double[3];
+ */
+ private static double[] bHat = new double[3];
- /*
+ /*
* These are the original error estimator coefficients.
- */
+ */
+ static {
+ bHat[0] = (1.0 - w) / 3.0;
+ bHat[1] = (3.0 * w + 1.0) / 3.0;
+ bHat[2] = d / 3.0;
+ }
- static {
- bHat[0] = (1.0 - w) / 3.0;
- bHat[1] = (3.0 * w + 1.0) / 3.0;
- bHat[2] = d / 3.0;
- }
+ private final static double[] bbHat = new double[]{w - bHat[0], w - bHat[1], d - bHat[2]};
- private final static double[] bbHat = new double[] { w - bHat[0], w - bHat[1], d - bHat[2] };
+ private double k[][];
- private double k[][];
+ private double[] inward;
+ private double[] bVector; // right-hand side of linear set A * x = B
+ private double[] est; // error estimator
+ private double[][] aMatrix; // matrix of linear set A * x = B
- private double[] inward;
- private double[] bVector; // right-hand side of linear set A * x = B
- private double[] est; // error estimator
- private double[][] aMatrix; // matrix of linear set A * x = B
+ private SquareMatrix invA; // inverse matrix
+ private Vector i2; // second stage (trapezoidal)
+ private Vector i3; // third stage (backward-difference second order)
- private SquareMatrix invA; // inverse matrix
- private Vector i2; // second stage (trapezoidal)
- private Vector i3; // third stage (backward-difference second order)
-
- /*
+ /*
* Constants for third-stage calculation
- */
-
- private double w_d = w / d;
- private double _1w_d = (1.0 - w_d);
-
- public TRBDF2(Discretisation intensities) {
- super(intensities);
- }
-
- @Override
- public RTECalculationStatus integrate() {
- final int nH = getDiscretisation().getOrdinates().getHalfLength();
-
- bVector = new double[nH];
- est = new double[nH];
- aMatrix = new double[nH][nH];
- inward = new double[nH];
-
- k = new double[3][nH];
- return super.integrate();
- }
-
- /**
- * Generates a non-uniform (stretched at boundaries) grid using the
- * argument as the density.
- * @param nNew new grid density
- */
-
- @Override
- public void generateGrid(int nNew) {
- getDiscretisation().getGrid().generate(nNew);
- }
-
- /**
- * Performs a TRBDF2 step.
- */
-
- @Override
- public Vector[] step(final int j, final double sign) {
- var intensities = getDiscretisation();
- var quantities = intensities.getQuantities();
- var hermite = getHermiteInterpolator();
-
- final double h = sign * intensities.getGrid().step(j, sign);
- hermite.bMinusA = h; // <---- for Hermite interpolation
-
- final int total = getDiscretisation().getOrdinates().getTotalNodes();
- final int increment = (int) (1 * sign);
- final double t = intensities.getGrid().getNode(j);
- hermite.a = t; // <---- for Hermite interpolation
-
- /*
+ */
+ private double w_d = w / d;
+ private double _1w_d = (1.0 - w_d);
+
+ public TRBDF2(Discretisation intensities) {
+ super(intensities);
+ }
+
+ @Override
+ public RTECalculationStatus integrate() {
+ final int nH = getDiscretisation().getOrdinates().getHalfLength();
+
+ bVector = new double[nH];
+ est = new double[nH];
+ aMatrix = new double[nH][nH];
+ inward = new double[nH];
+
+ k = new double[3][nH];
+ return super.integrate();
+ }
+
+ /**
+ * Generates a non-uniform (stretched at boundaries) grid using the argument
+ * as the density.
+ *
+ * @param nNew new grid density
+ */
+ @Override
+ public void generateGrid(int nNew) {
+ getDiscretisation().getGrid().generate(nNew);
+ }
+
+ /**
+ * Performs a TRBDF2 step.
+ */
+ @Override
+ public Vector[] step(final int j, final double sign) {
+ var intensities = getDiscretisation();
+ var quantities = intensities.getQuantities();
+ var hermite = getHermiteInterpolator();
+
+ final double h = sign * intensities.getGrid().step(j, sign);
+ hermite.bMinusA = h; // <---- for Hermite interpolation
+
+ final int total = getDiscretisation().getOrdinates().getTotalNodes();
+ final int increment = (int) (1 * sign);
+ final double t = intensities.getGrid().getNode(j);
+ hermite.a = t; // <---- for Hermite interpolation
+
+ /*
* Indices of OUTWARD intensities (n1 <= i < n2)
- */
-
- final int nPositiveStart = intensities.getOrdinates().getFirstPositiveNode();
- final int nNegativeStart = intensities.getOrdinates().getFirstNegativeNode();
- final int halfLength = nNegativeStart - nPositiveStart;
- final int n1 = sign > 0 ? nPositiveStart : nNegativeStart; // either first positive index or first negative
- final int n2 = sign > 0 ? nNegativeStart : total; // either first negative index or n
-
- /*
+ */
+ final int nPositiveStart = intensities.getOrdinates().getFirstPositiveNode();
+ final int nNegativeStart = intensities.getOrdinates().getFirstNegativeNode();
+ final int halfLength = nNegativeStart - nPositiveStart;
+ final int n1 = sign > 0 ? nPositiveStart : nNegativeStart; // either first positive index or first negative
+ final int n2 = sign > 0 ? nNegativeStart : total; // either first negative index or n
+
+ /*
* Indices of INWARD intensities (n3 <= i < n4)
- */
+ */
+ final int n3 = total - n2; // either first negative index or 0 (for INWARD intensities)
+ final int n4 = total - n1; // either n or first negative index (for INWARD intensities)
+ final int n5 = nNegativeStart - n3; // either 0 or first negative index
- final int n3 = total - n2; // either first negative index or 0 (for INWARD intensities)
- final int n4 = total - n1; // either n or first negative index (for INWARD intensities)
- final int n5 = nNegativeStart - n3; // either 0 or first negative index
-
- /*
+ /*
* Try to use FSAL
- */
-
- if (!isFirstRun()) { // if this is not the first step
+ */
+ if (!isFirstRun()) { // if this is not the first step
- for (int l = n1; l < n2; l++) {
- k[0][l - n1] = quantities.getQLast(l - n1);
- }
+ for (int l = n1; l < n2; l++) {
+ k[0][l - n1] = quantities.getQLast(l - n1);
+ }
- } else {
+ } else {
- for (int l = n1; l < n2; l++) {
- k[0][l - n1] = super.derivative(l, j, t, quantities.getIntensity(j, l)); // first-stage right-hand
- // side: f( t, In)
- // )
- } // )
+ for (int l = n1; l < n2; l++) {
+ k[0][l - n1] = super.derivative(l, j, t, quantities.getIntensity(j, l)); // first-stage right-hand
+ // side: f( t, In)
+ // )
+ } // )
- setFirstRun(false);
+ setFirstRun(false);
- }
+ }
- /*
+ /*
* ============================= 1st and 2nd stages begin here
* =============================
- */
-
- final double hd = h * d;
- final double tPlusGamma = t + gamma * h;
+ */
+ final double hd = h * d;
+ final double tPlusGamma = t + gamma * h;
- /*
+ /*
* Interpolate INWARD intensities at t + gamma*h (second stage)
- */
-
- for (int i = 0; i < inward.length; i++) {
- hermite.y0 = quantities.getIntensity(j, i + n3);
- hermite.y1 = quantities.getIntensity(j + increment, i + n3);
- hermite.d0 = quantities.getDerivative(j, i + n3);
- hermite.d1 = quantities.getDerivative(j + increment, i + n3);
- inward[i] = hermite.interpolate(tPlusGamma);
- }
-
- /*
+ */
+ for (int i = 0; i < inward.length; i++) {
+ hermite.y0 = quantities.getIntensity(j, i + n3);
+ hermite.y1 = quantities.getIntensity(j + increment, i + n3);
+ hermite.d0 = quantities.getDerivative(j, i + n3);
+ hermite.d1 = quantities.getDerivative(j + increment, i + n3);
+ inward[i] = hermite.interpolate(tPlusGamma);
+ }
+
+ /*
* Trapezoidal step
- */
+ */
+ final double prefactorNumerator = -hd * getPhaseFunction().getHalfAlbedo();
- final double prefactorNumerator = -hd * getPhaseFunction().getHalfAlbedo();
-
- double matrixPrefactor;
- final var ordinates = intensities.getOrdinates();
+ double matrixPrefactor;
+ final var ordinates = intensities.getOrdinates();
- for (int i = 0; i < halfLength; i++) {
+ for (int i = 0; i < halfLength; i++) {
- quantities.setDerivative(j, i + n1, k[0][i]); // store derivatives for Hermite interpolation
+ quantities.setDerivative(j, i + n1, k[0][i]); // store derivatives for Hermite interpolation
- bVector[i] = quantities.getIntensity(j, i + n1)
- + hd * (k[0][i] + partial(i + n1, tPlusGamma, inward, n3, n4)); // only
- // INWARD
- // intensities
+ bVector[i] = quantities.getIntensity(j, i + n1)
+ + hd * (k[0][i] + partial(i + n1, tPlusGamma, inward, n3, n4)); // only
+ // INWARD
+ // intensities
- matrixPrefactor = prefactorNumerator / ordinates.getNode(i + n1);
+ matrixPrefactor = prefactorNumerator / ordinates.getNode(i + n1);
- // all elements
- for (int k = 0; k < aMatrix[0].length; k++) {
- aMatrix[i][k] = matrixPrefactor * ordinates.getWeight(k + n5)
- * getPhaseFunction().function(i + n1, k + n5); // only
- // OUTWARD
- // (and zero)
- // intensities
- }
+ // all elements
+ for (int k = 0; k < aMatrix[0].length; k++) {
+ aMatrix[i][k] = matrixPrefactor * ordinates.getWeight(k + n5)
+ * getPhaseFunction().function(i + n1, k + n5); // only
+ // OUTWARD
+ // (and zero)
+ // intensities
+ }
- // additionally for the diagonal elements
- aMatrix[i][i] += 1.0 + hd / ordinates.getNode(i + n1);
+ // additionally for the diagonal elements
+ aMatrix[i][i] += 1.0 + hd / ordinates.getNode(i + n1);
- }
+ }
- invA = (Matrices.createMatrix(aMatrix)).inverse(); // this matrix is re-used for subsequent stages
- i2 = invA.multiply(new Vector(bVector)); // intensity vector at 2nd stage
+ invA = (Matrices.createSquareMatrix(aMatrix)).inverse(); // this matrix is re-used for subsequent stages
+ i2 = invA.multiply(new Vector(bVector)); // intensity vector at 2nd stage
- /*
+ /*
* ================== Third stage (BDF2) ==================
- */
-
- final double th = t + h;
+ */
+ final double th = t + h;
- for (int i = 0; i < aMatrix.length; i++) {
+ for (int i = 0; i < aMatrix.length; i++) {
- bVector[i] = quantities.getIntensity(j, i + n1) * _1w_d + w_d * i2.get(i)
- + hd * partial(i + n1, j + increment, th, n3, n4); // only INWARD intensities at node j + 1 (i.e. no
- // interpolation)
- k[1][i] = (i2.get(i) - quantities.getIntensity(j, i + n1)) / hd - k[0][i];
+ bVector[i] = quantities.getIntensity(j, i + n1) * _1w_d + w_d * i2.get(i)
+ + hd * partial(i + n1, j + increment, th, n3, n4); // only INWARD intensities at node j + 1 (i.e. no
+ // interpolation)
+ k[1][i] = (i2.get(i) - quantities.getIntensity(j, i + n1)) / hd - k[0][i];
- }
+ }
- i3 = invA.multiply(new Vector(bVector));
+ i3 = invA.multiply(new Vector(bVector));
- for (int i = 0; i < aMatrix.length; i++) {
- k[2][i] = (i3.get(i) - quantities.getIntensity(j, i + n1)
- - w_d * (i2.get(i) - quantities.getIntensity(j, i + n1))) / hd;
- quantities.setQLast(i, k[2][i]);
- est[i] = (bbHat[0] * k[0][i] + bbHat[1] * k[1][i] + bbHat[2] * k[2][i]) * h;
- }
+ for (int i = 0; i < aMatrix.length; i++) {
+ k[2][i] = (i3.get(i) - quantities.getIntensity(j, i + n1)
+ - w_d * (i2.get(i) - quantities.getIntensity(j, i + n1))) / hd;
+ quantities.setQLast(i, k[2][i]);
+ est[i] = (bbHat[0] * k[0][i] + bbHat[1] * k[1][i] + bbHat[2] * k[2][i]) * h;
+ }
- return new Vector[] { i3, invA.multiply(new Vector(est)) };
+ return new Vector[]{i3, invA.multiply(new Vector(est))};
- }
+ }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/pulse/problem/schemes/rte/dom/package-info.java b/src/main/java/pulse/problem/schemes/rte/dom/package-info.java
index 42f5716b..4c5177ea 100644
--- a/src/main/java/pulse/problem/schemes/rte/dom/package-info.java
+++ b/src/main/java/pulse/problem/schemes/rte/dom/package-info.java
@@ -4,5 +4,4 @@
* includes ODE solvers, ordinate set handlers, non-uniform grid, iterative
* solvers, and scattering phase functions.
*/
-
-package pulse.problem.schemes.rte.dom;
\ No newline at end of file
+package pulse.problem.schemes.rte.dom;
diff --git a/src/main/java/pulse/problem/schemes/rte/exact/ChandrasekharsQuadrature.java b/src/main/java/pulse/problem/schemes/rte/exact/ChandrasekharsQuadrature.java
index 2fb41182..535a603e 100644
--- a/src/main/java/pulse/problem/schemes/rte/exact/ChandrasekharsQuadrature.java
+++ b/src/main/java/pulse/problem/schemes/rte/exact/ChandrasekharsQuadrature.java
@@ -11,9 +11,8 @@
import static pulse.properties.NumericProperty.requireType;
import static pulse.properties.NumericPropertyKeyword.QUADRATURE_POINTS;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
+import java.util.Set;
import java.util.stream.IntStream;
import org.apache.commons.math3.analysis.solvers.LaguerreSolver;
@@ -24,252 +23,253 @@
import pulse.math.linear.Vector;
import pulse.properties.NumericProperty;
import pulse.properties.NumericPropertyKeyword;
-import pulse.properties.Property;
/**
* This quadrature methods of evaluating the composition product of the
* exponential integral and blackbody spectral power spectrum has been given by
* Chandrasekhar and is based on constructing a moment matrix.
- *
+ *
* @see Chandrasekhar,
- * S. Radiative transfer
- *
+ * S. Radiative transfer
+ *
*/
-
public class ChandrasekharsQuadrature extends CompositionProduct {
- private int m;
- private double expLower;
- private double expUpper;
- private LaguerreSolver solver;
- private double[] moments;
-
- /**
- * Constructs a {@code ChandrasekharsQuadrature} object with a default number of
- * nodes, a {@code LaguerreSolver} with default precision and integration bounds
- * set to [0,1].
- */
-
- public ChandrasekharsQuadrature() {
- super(new Segment(0, 1));
- m = (int) def(QUADRATURE_POINTS).getValue();
- solver = new LaguerreSolver();
- }
-
- @Override
- public double integrate() {
- var bounds = this.transformedBounds();
- expLower = -exp(-bounds[0]);
- expUpper = -exp(-bounds[1]);
-
- double[] roots = roots();
-
- Vector weights = weights(roots);
-
- return f(roots).dot(weights) / getBeta();
- }
-
- public NumericProperty getQuadraturePoints() {
- return derive(QUADRATURE_POINTS, m);
- }
-
- public void setQuadraturePoints(NumericProperty m) {
- requireType(m, QUADRATURE_POINTS);
- this.m = (int) m.getValue();
- }
-
- @Override
- public void set(NumericPropertyKeyword type, NumericProperty property) {
- if (type == QUADRATURE_POINTS) {
- setQuadraturePoints(property);
- firePropertyChanged(this, property);
- }
- }
-
- @Override
- public List listedTypes() {
- return new ArrayList