Skip to content

Commit

Permalink
Fixed two problems
Browse files Browse the repository at this point in the history
-Error in the LMOptimiser where the computeJacobian was not thread-safe.
This has been transferred to LMPath.
- Fixed NetzschCSVReader, so that it now reads sample thickness
  • Loading branch information
Artem Lunev committed May 26, 2021
1 parent aaf7e8e commit fb64ec9
Show file tree
Hide file tree
Showing 21 changed files with 305 additions and 121 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>kotik-coder</groupId>
<artifactId>PULsE</artifactId>
<version>1.90</version>
<version>1.91</version>
<name>PULsE</name>
<description>Processing Unit for Laser flash Experiments</description>
<build>
Expand Down
20 changes: 14 additions & 6 deletions src/main/java/pulse/io/readers/NetzschCSVReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import pulse.input.ExperimentalData;
import pulse.input.Metadata;
import pulse.input.Range;
import pulse.properties.NumericPropertyKeyword;
import pulse.ui.Messages;

/**
Expand All @@ -32,13 +33,15 @@ public class NetzschCSVReader implements CurveReader {

private final static double TO_KELVIN = 273;
protected final static double TO_SECONDS = 1E-3;
private final static double TO_METRES = 1E-3;

private final static String SAMPLE_TEMPERATURE = "Sample_temperature";
protected final static String SHOT_DATA = "Shot_data";
private final static String SHOT_DATA = "Shot_data";
private final static String DETECTOR = "DETECTOR";
private final static String THICKNESS = "Thickness_RT";

/**
* Note comma is included a delimiter character here.
* Note comma is included as a delimiter character here.
*/

public final static String delims = "[#();,/°Cx%^]+";
Expand All @@ -62,8 +65,8 @@ public String getSupportedExtension() {
* This will throw an {@code IllegalArgumentException} if the first entry in this file does not contain the
* {@value SHOT_DATA} string. If this is found, then an ID is extracted from the file, which will then be used
* to associate a pulse with the newly create {@code ExperimentalData} (this requires another reader.
* When the ID is identified, the file is searched for the keyword {@value SAMPLE_TEMPERATURE} to determine
* the baseline temperature of the shot. Then the method proceeds to search for the {@code DETECTOR} keyword,
* When the ID is identified, the file is searched for the keywords {@value THICKNESS} and {@value SAMPLE_TEMPERATURE} to
* determine the sample thickness and baseline temperature of the shot. Then the method proceeds to search for the {@code DETECTOR} keyword,
* marking the beginning of the experimental time-signal sequence. If, for example, the file only contains
* the pulse data, the method will return an empty list and print an error message in the log, saying that the file
* was skipped. Otherwise, the time-signal sequence will be read, taking care to convert the time (in milliseconds
Expand All @@ -84,8 +87,11 @@ public List<ExperimentalData> read(File file) throws IOException {

int shotId = determineShotID(reader, file);

String[] tempTokens = findLineByLabel(reader, SAMPLE_TEMPERATURE, delims).split(delims);
double sampleTemperature = Double.parseDouble( tempTokens[tempTokens.length - 1] ) + TO_KELVIN;
var tempTokens = findLineByLabel(reader, THICKNESS, delims).split(delims);
final double thickness = Double.parseDouble( tempTokens[tempTokens.length - 1] ) * TO_METRES;

tempTokens = findLineByLabel(reader, SAMPLE_TEMPERATURE, delims).split(delims);
final double sampleTemperature = Double.parseDouble( tempTokens[tempTokens.length - 1] ) + TO_KELVIN;

/*
* Finds the detector keyword.
Expand All @@ -102,6 +108,8 @@ public List<ExperimentalData> read(File file) throws IOException {
populate(curve, reader);

var met = new Metadata(derive(TEST_TEMPERATURE, sampleTemperature), shotId);
met.set(NumericPropertyKeyword.THICKNESS, derive(NumericPropertyKeyword.THICKNESS, thickness));

curve.setMetadata(met);
curve.setRange(new Range(curve.getTimeSequence()));

Expand Down
47 changes: 47 additions & 0 deletions src/main/java/pulse/math/ParameterVector.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public ParameterVector(ParameterVector proto, Vector v) {

}

/**
* Copy constructor
* @param v another vector
*/

public ParameterVector(ParameterVector v) {
this( v.dimension() );
final int n = dimension();
Expand All @@ -57,6 +62,11 @@ public ParameterVector(ParameterVector v) {
System.arraycopy(v.transforms, 0, transforms, 0, n);
System.arraycopy(v.bounds, 0, bounds, 0, n);
}

/**
* Creates an empty ParameterVector with a dimension of {@code n}
* @param n dimension
*/

private ParameterVector(final int n) {
super(n);
Expand All @@ -75,6 +85,14 @@ public void set(final int i, final double x) {
set(i, x, false);
}

/**
* Sets the <math><i>i</i></math>-component of this vector to {@code x} or its corresponding transform,
* if the latter is defined and {@code ignoreTransform} is {@code false}.
* @param i index of the value and its transform
* @param x the non-transformed value, which needs to be assigned to the i-th component
* @param ignoreTransform if {@code} false, will ignore exiting transform.
*/

public void set(final int i, final double x, boolean ignoreTransform) {
final double t = ignoreTransform || transforms[i] == null ? x : transforms[i].transform(x);
super.set(i, t);
Expand Down Expand Up @@ -113,10 +131,22 @@ public double getParameterValue(NumericPropertyKeyword index) {
return super.get(indexOf(index));
}

/**
* Performs an inverse transform corresponding to the index {@code i} of this vector.
* @param i the index of the transform
* @return the inverse transform of {@code get(i) } if the transform is defined, {@code get(i)} otherwise.
*/

public double inverseTransform(final int i) {
return transforms[i] != null ? transforms[i].inverse( get(i) ) : get(i);
}

/**
* Gets the transformable of the i-th component
* @param i index of the component
* @return the corresponding {@code Transforamble}
*/

public Transformable getTransform(final int i) {
return transforms[i];
}
Expand All @@ -129,13 +159,25 @@ public Segment getParameterBounds(final int i) {
return bounds[i];
}

/**
* If transform of {@code i} is not null, applies the transformation to the component bounds
* @param i the index of the component
* @return the transformed bounds
*/

public Segment getTransformedBounds(final int i) {
return transforms[i] != null ?
new Segment( transforms[i].transform( bounds[i].getMinimum() ),
transforms[i].transform( bounds[i].getMaximum() ) ) :
getParameterBounds(i);
}

/**
* Sets the bounds of i-th component of this vector.
* @param i the index of the component
* @param segment new parameter bounds
*/

public void setParameterBounds(int i, Segment segment) {
bounds[i] = segment;
}
Expand All @@ -149,6 +191,11 @@ public void setParameterBounds(int i, Segment segment) {
public List<NumericPropertyKeyword> getIndices() {
return Arrays.asList(indices);
}

/**
* This will assign a new list of indices to this vector
* @param indices a list of indices
*/

private void assign(List<NumericPropertyKeyword> indices) {
this.indices = indices.toArray(new NumericPropertyKeyword[indices.size()]);
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/pulse/math/transforms/AtanhTransform.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,34 @@
import pulse.math.Segment;

/**
* Hyper-tangent parameter transform.
* Hyper-tangent parameter transform allowing to set an upper bound for a parameter.
*/

public class AtanhTransform extends BoundedParameterTransform {

/**
* Only the upper bound of the argument is used.
* @param bounds the {@code bounda.getMaximum()} is used in the transforms
*/

public AtanhTransform(Segment bounds) {
super(bounds);
}

/**
* @see pulse.math.MathUtils.atanh()
* @see pulse.math.Segment.getBounds()
*/

@Override
public double transform(double a) {
return atanh(2.0 * a / getBounds().getMaximum() - 1.0);
}

/**
* @see pulse.math.MathUtils.tanh()
* @see pulse.math.Segment.getBounds()
*/

@Override
public double inverse(double t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

import pulse.math.Segment;

/**
* An abstract {@code Transformable} where the bounds of the parameter is manually set.
* Subclasses can be bounded from either on or both sides.
*
*/

public abstract class BoundedParameterTransform implements Transformable {

private Segment bounds;
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/pulse/math/transforms/InvLenTransform.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

import pulse.problem.statements.model.ThermalProperties;

/**
* A transform that simply divides the value by the length of the
* sample.
*/

public class InvLenTransform implements Transformable {

private double l;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import static java.lang.Math.log;
import static java.lang.Math.sqrt;

/**
* A utility class containing standard mathematical transforms and their inverses for non-bounded parameters.
*
*/

public class StandardTransformations {

/**
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/pulse/math/transforms/Transformable.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
package pulse.math.transforms;

/**
* An interface for performing reversible one-to-one mapping of the model parameters.
*
*/

public interface Transformable {

/**
* Performs the selected transform with {@code value}
* @param value a double representing the parameter value
* @return the results, such that {@code inverse( transform(value) ) = value}
*/

public double transform(double value);

/**
* Inverses the transform.
*/

public double inverse(double t);

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ public ExponentiallyModifiedGaussian(ExponentiallyModifiedGaussian another) {
@Override
public void init(ExperimentalData data, DiscretePulse pulse) {
super.init(data, 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.
}
Expand All @@ -83,6 +82,12 @@ public double evaluateAt(double time) {
* 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 List<Property> listedTypes() {
Expand Down Expand Up @@ -170,8 +175,7 @@ public void set(NumericPropertyKeyword type, NumericProperty property) {

@Override
public PulseTemporalShape copy() {
// TODO Auto-generated method stub
return null;
return new ExponentiallyModifiedGaussian(this);
}

}
36 changes: 36 additions & 0 deletions src/main/java/pulse/problem/laser/NumericPulse.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
import pulse.properties.NumericPropertyKeyword;
import pulse.tasks.SearchTask;

/**
* A numeric pulse is given by a set of discrete {@code NumericPulseData} measured
* independelty using a pulse diode.
* @see pulse.problem.laser.NumericPulseData
*
*/

public class NumericPulse extends PulseTemporalShape {

private NumericPulseData pulseData;
Expand All @@ -22,11 +29,24 @@ 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.
* @see normalise()
*
*/

@Override
public void init(ExperimentalData data, DiscretePulse pulse) {
pulseData = data.getMetadata().getPulseData();
Expand All @@ -43,6 +63,13 @@ public void init(ExperimentalData data, DiscretePulse pulse) {
normalise(problem);
}

/**
* Checks that the area of the pulse curve is unity (within a small error margin).
* If this is {@code false}, re-scales the numeric data using {@code 1/area} as the scaling factor.
* @param problem defines the {@code timeFactor} needed for re-building the interpolation
* @see pulse.problem.laser.NumericPulseData.scale()
*/

public void normalise(Problem problem) {

final double EPS = 1E-2;
Expand Down Expand Up @@ -76,6 +103,11 @@ private void doInterpolation(double timeFactor) {

}

/**
* If the argument is less than the pulse width, uses the spline function to interpolated the pulse
* function at {@code time}. Otherwise returns zero.
*/

@Override
public double evaluateAt(double time) {
return time > adjustedPulseWidth ? 0.0 : interpolation.value(time);
Expand All @@ -86,6 +118,10 @@ public PulseTemporalShape copy() {
return new NumericPulse();
}

/**
* Does not define any property.
*/

@Override
public void set(NumericPropertyKeyword type, NumericProperty property) {
// TODO Auto-generated method stub
Expand Down
Loading

0 comments on commit fb64ec9

Please sign in to comment.