Skip to content

Commit cf6e02f

Browse files
authored
Fixes #1863 remove check for monotonically increasing time values? (#2764)
* Fixes #1863 remove check for monotonically increasing time values? * add a comment * pr feedback * pr feedback * simplify * reduce iteration * simplify
1 parent 01a7814 commit cf6e02f

File tree

14 files changed

+167
-537
lines changed

14 files changed

+167
-537
lines changed
Lines changed: 29 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
using System;
22
using System.Collections.Generic;
3-
using OSPSuite.Assets;
43
using OSPSuite.Core.Domain.UnitSystem;
4+
using OSPSuite.Utility.Extensions;
55

66
namespace OSPSuite.Core.Domain.Data
77
{
8-
/// <summary>
9-
/// BaseGrid must be a strictly monotonic increasing sequence
10-
/// </summary>
118
public class BaseGrid : DataColumn
129
{
1310
[Obsolete("For serialization")]
@@ -26,7 +23,7 @@ public BaseGrid(string id, string name, IDimension dimension) : base(id, name, d
2623
BaseGrid = this;
2724
_values = new List<float>();
2825
var defaultUnitName = dimension != null ? dimension.DefaultUnitName : string.Empty;
29-
DataInfo = new DataInfo(ColumnOrigins.BaseGrid) {DisplayUnitName = defaultUnitName};
26+
DataInfo = new DataInfo(ColumnOrigins.BaseGrid) { DisplayUnitName = defaultUnitName };
3027
QuantityInfo.Type = QuantityType.BaseGrid;
3128
}
3229

@@ -35,18 +32,6 @@ public override IReadOnlyList<float> Values
3532
get => _values;
3633
set
3734
{
38-
//check strong monotony of values first (values are sorted and unique)
39-
for (int i = 1; i < value.Count; i++)
40-
{
41-
if (value[i] <= value[i - 1])
42-
{
43-
44-
var beforeValue =this.ConvertToDisplayUnit(value[i - 1]);
45-
var afterValue = this.ConvertToDisplayUnit(value[i]);
46-
throw new TimeNotStrictlyMonotoneException(beforeValue, afterValue, DisplayUnit.Name, Repository?.Name);
47-
}
48-
}
49-
5035
_values.Clear();
5136
_values.AddRange(value);
5237
}
@@ -60,43 +45,6 @@ public override List<float> InternalValues
6045

6146
public override bool HasSingleValue => false;
6247

63-
/// <summary>
64-
/// Insert the given values and returns the index where the value should be inserted
65-
/// </summary>
66-
public virtual void Insert(float value)
67-
{
68-
int index = _values.BinarySearch(value);
69-
if (index < 0) _values.Insert(~index, value);
70-
}
71-
72-
internal virtual int InsertOrReplace(float value, out bool replaced)
73-
{
74-
int index = _values.BinarySearch(value);
75-
replaced = true;
76-
if (index < 0)
77-
{
78-
index = ~index;
79-
_values.Insert(index, value);
80-
replaced = false;
81-
}
82-
83-
return index;
84-
}
85-
86-
public virtual void InsertInterval(float start, float end, int numberOfTimePoints)
87-
{
88-
if (end <= start) throw new InvalidArgumentException("end <= start");
89-
if (numberOfTimePoints < 2) throw new InvalidArgumentException("numberOfTimePoints < 2");
90-
91-
float timeInterval = (end - start) / (numberOfTimePoints - 1);
92-
for (int i = 0; i < numberOfTimePoints - 1; i++)
93-
{
94-
Insert(start + i * timeInterval);
95-
}
96-
97-
Insert(end);
98-
}
99-
10048
public virtual bool Remove(float value)
10149
{
10250
return _values.Remove(value);
@@ -109,21 +57,40 @@ public virtual void Clear()
10957

11058
public virtual int Count => _values.Count;
11159

112-
public virtual int IndexOf(float value)
60+
public virtual IReadOnlyList<int> IndexesOf(float value)
11361
{
114-
return _values.IndexOf(value);
62+
var indexes = new List<int>();
63+
_values.Each((x, i) =>
64+
{
65+
if (x == value)
66+
indexes.Add(i);
67+
});
68+
69+
return indexes;
11570
}
11671

117-
public virtual int LeftIndexOf(float value)
72+
public virtual int IndexOfNextLowest(float value)
11873
{
119-
int index = _values.BinarySearch(value);
120-
return index >= 0 ? index : ~index - 1;
74+
int nextLowestIndex = -1;
75+
Values.Each((x, i) =>
76+
{
77+
if (x <= value && (nextLowestIndex < 0 || x > Values[nextLowestIndex]))
78+
nextLowestIndex = i;
79+
});
80+
81+
return nextLowestIndex;
12182
}
12283

123-
public virtual int RightIndexOf(float value)
84+
public virtual int IndexOfNextHighest(float value)
12485
{
125-
int index = _values.BinarySearch(value);
126-
return index >= 0 ? index : ~index;
86+
int nextHighestIndex = Values.Count;
87+
Values.Each((x, i) =>
88+
{
89+
if (x >= value && (nextHighestIndex >= Values.Count || x < Values[nextHighestIndex]))
90+
nextHighestIndex = i;
91+
});
92+
93+
return nextHighestIndex;
12794
}
12895
}
12996
}

src/OSPSuite.Core/Domain/Data/DataColumn.cs

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using OSPSuite.Core.Extensions;
77
using OSPSuite.Utility.Collections;
88
using OSPSuite.Utility.Exceptions;
9+
using OSPSuite.Utility.Extensions;
910
using OSPSuite.Utility.Reflection;
1011

1112
namespace OSPSuite.Core.Domain.Data
@@ -216,25 +217,42 @@ public virtual float GetValue(float baseValue)
216217
if (Values == null || !Values.Any())
217218
return float.NaN;
218219

219-
int index = BaseGrid.IndexOf(baseValue);
220-
if (index >= 0)
221-
return Values[index];
220+
var indexes = BaseGrid.IndexesOf(baseValue);
221+
if (indexes.Any())
222+
return averageFor(indexes);
222223

223-
int leftIndex = BaseGrid.LeftIndexOf(baseValue);
224-
int rightIndex = BaseGrid.RightIndexOf(baseValue);
224+
var indexOfNextLowest = BaseGrid.IndexOfNextLowest(baseValue);
225+
var indexOfNextHighest = BaseGrid.IndexOfNextHighest(baseValue);
225226

226-
if (leftIndex < 0)
227+
if (indexOfNextLowest < 0 || indexOfNextHighest >= BaseGrid.Count || indexOfNextLowest == indexOfNextHighest)
227228
return float.NaN;
228229

229-
if (rightIndex >= BaseGrid.Count)
230-
return float.NaN;
230+
var valueOfNextLowest = BaseGrid[indexOfNextLowest];
231+
var valueOfNextHighest = BaseGrid[indexOfNextHighest];
231232

232-
if (leftIndex == rightIndex)
233-
return float.NaN;
233+
var averageOfNextLowest = averageFor(BaseGrid.IndexesOf(valueOfNextLowest));
234+
var averageOfNextHighest = averageFor(BaseGrid.IndexesOf(valueOfNextHighest));
235+
236+
return averageOfNextLowest +
237+
(averageOfNextHighest - averageOfNextLowest) * (baseValue - valueOfNextLowest) /
238+
(valueOfNextHighest - valueOfNextLowest);
239+
}
240+
241+
/// <summary>
242+
/// Averages the values for all indexes in <paramref name="indexesToAverage"/>
243+
/// </summary>
244+
/// <param name="indexesToAverage"></param>
245+
/// <returns>The mean average value for all indexes</returns>
246+
private float averageFor(IReadOnlyList<int> indexesToAverage)
247+
{
248+
var accumulator = 0f;
249+
250+
indexesToAverage.Each(x =>
251+
{
252+
accumulator += Values[x];
253+
});
234254

235-
return Values[leftIndex] +
236-
(Values[rightIndex] - Values[leftIndex]) * (baseValue - BaseGrid[leftIndex]) /
237-
(BaseGrid[rightIndex] - BaseGrid[leftIndex]);
255+
return accumulator / indexesToAverage.Count;
238256
}
239257

240258
public override string ToString() => Name;

src/OSPSuite.Core/Domain/Data/DataRepository.cs

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -149,60 +149,8 @@ public string ExtendedPropertyValueFor(string propertyName)
149149
return hasExtendedPropertyFor(propertyName) ? ExtendedProperties[propertyName].ValueAsObject.ConvertedTo<string>() : null;
150150
}
151151

152-
/// <summary>
153-
/// Inserts the values defined in <paramref name="columnValues" /> at the index found in the base column for the value
154-
/// <paramref name="baseValue" />.
155-
/// Values will be either replaced (there is already an entry with the same time exactly) or inserted at the right index
156-
/// to prevent the required base grid monotony.
157-
/// In the case of an insertion, a <c>float.Nan</c> values will be inserted for columns using the base grid and for
158-
/// which a value was not provided in <paramref name="columnValues" />
159-
/// </summary>
160-
/// <param name="baseValue">Value (e.g. time value) in base unit for which column values should be updated/inserted</param>
161-
/// <param name="columnValues">Cache containing the id of the columns to update as key and the corresponding value.</param>
162-
public void InsertValues(float baseValue, Cache<string, float> columnValues)
163-
{
164-
var baseGrid = BaseGrid;
165-
var index = baseGrid.InsertOrReplace(baseValue, out bool replaced);
166-
167-
foreach (var column in columnValues.KeyValues.Where(x => Contains(x.Key)).Select(x => GetColumn(x.Key)))
168-
{
169-
var value = columnValues[column.Id];
170-
if (replaced)
171-
column[index] = value;
172-
else
173-
column.InsertValueAt(index, columnValues[column.Id]);
174-
}
175-
176-
if (replaced) return;
177-
178-
//we need to add a dummy values for all other columns not in column values using the basegrid
179-
foreach (var column in columnsThatWereNotUpdated(columnValues))
180-
{
181-
column.InsertValueAt(index, float.NaN);
182-
}
183-
}
184-
185-
/// <summary>
186-
/// Swaps out all values defined in the <see cref="DataRepository" /> at the index for the baseGrid
187-
/// <paramref name="oldBaseGridValue" /> and insert them back at the index for <paramref name="newBaseGridValue" />
188-
/// </summary>
189-
/// <param name="oldBaseGridValue">BaseGrid value that will be used to retrieve the index of values to swap out </param>
190-
/// <param name="newBaseGridValue">BaseGrid value that will be used to retrieve the index where the values will be inserted</param>
191-
public virtual void SwapValues(float oldBaseGridValue, float newBaseGridValue)
192-
{
193-
var index = BaseGrid.IndexOf(oldBaseGridValue);
194-
if (index < 0) return;
195-
196-
var columnValues = new Cache<string, float>();
197-
AllButBaseGrid().Each(c => columnValues.Add(c.Id, c[index]));
198-
RemoveValuesAt(index);
199-
InsertValues(newBaseGridValue, columnValues);
200-
}
201-
202152
public virtual void RemoveValuesAt(int index) => _allColumns.Each(c => c.RemoveValueAt(index));
203153

204-
private IEnumerable<DataColumn> columnsThatWereNotUpdated(Cache<string, float> columnValues) => AllButBaseGrid().Where(x => !columnValues.Contains(x.Id));
205-
206154
public IEnumerable<DataColumn> AllButBaseGrid() => _allColumns.Where(x => !x.IsBaseGrid());
207155

208156
/// <summary>

src/OSPSuite.Core/Domain/Services/ParameterIdentifications/JacobianMatrixCalculator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ private static IEnumerable<JacobianRow> jacobianRowFrom(OptimizationRunResult ru
5151
var timeGrid = outputResult.BaseGrid;
5252

5353
return from residual in runResult.AllResidualsFor(fullOutputPath).Where(x => x.Weight > 0)
54-
let timeIndex = timeGrid.LeftIndexOf(Convert.ToSingle(residual.Time))
54+
// We can use IndexOfNextLowest here since the calculation column will have monotonic increasing time values
55+
let timeIndex = timeGrid.IndexOfNextLowest(Convert.ToSingle(residual.Time))
5556
let outputValue = outputResult[timeIndex]
5657
let derivativeValues = retrievePartialDerivativeFor(allIdentificationParameters, x => retrievePartialDerivativeForResiduals(x, outputMapping, simModelBatch, timeIndex, outputValue, residual.Weight))
5758
select new JacobianRow(fullOutputPath, residual.Time, derivativeValues);

src/OSPSuite.Core/Domain/TimeNotStrictlyMonotoneException.cs

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/OSPSuite.Infrastructure.Import/Core/Mappers/DataSetToDataRepositoryMapper.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,7 @@ private bool convertParsedDataColumnAndReturnWarningFlag(DataRepository dataRepo
128128
dataInfo.LLOQ =
129129
Convert.ToSingle(dimension?.UnitValueToBaseUnitValue(dimension.FindUnit(lloqValue.Unit, ignoreCase: true), lloqValue.Lloq));
130130

131-
try
132-
{
133-
dataColumn.Values = values;
134-
}
135-
catch (TimeNotStrictlyMonotoneException ex)
136-
{
137-
// Catch and throw to add the sheet name
138-
throw new TimeNotStrictlyMonotoneException(ex, sheetName);
139-
}
131+
dataColumn.Values = values;
140132

141133
var propInfo = dataInfo.GetType().GetProperty(Constants.AUXILIARY_TYPE);
142134
var errorType = AuxiliaryType.Undefined;

src/OSPSuite.Presentation/Presenters/Importer/ImporterPresenter.cs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public ImporterPresenter(
7777
ISourceFilePresenter sourceFilePresenter,
7878
IDialogCreator dialogCreator,
7979
IPKMLPersistor pkmlPersistor,
80-
IDimensionMappingPresenter dimensionMappingPresenter,
80+
IDimensionMappingPresenter dimensionMappingPresenter,
8181
IDataSourceToDimensionSelectionDTOMapper dataSourceToDimensionSelectionDTOMapper) : base(view)
8282
{
8383
_importerDataPresenter = importerDataPresenter;
@@ -130,19 +130,8 @@ public ImporterPresenter(
130130

131131
private void plotDataSet(object sender, DataSetSelectedEventArgs e)
132132
{
133-
try
134-
{
135-
var dataRepository = _dataRepositoryMapper.ConvertImportDataSet(_dataSource.ImportedDataSetAt(e.Index));
136-
_previewPresenter.PlotDataRepository(dataRepository.DataRepository);
137-
}
138-
catch (TimeNotStrictlyMonotoneException timeNonMonotoneException)
139-
{
140-
var errors = new ParseErrors();
141-
errors.Add(_dataSource.DataSetAt(e.Index),
142-
new NonMonotonicalTimeParseErrorDescription(Error.ErrorWhenPlottingDataRepository(e.Index, timeNonMonotoneException.Message)));
143-
_importerDataPresenter.SetTabMarks(errors);
144-
_previewPresenter.SetViewingStateToError(timeNonMonotoneException.Message);
145-
}
133+
var dataRepository = _dataRepositoryMapper.ConvertImportDataSet(_dataSource.ImportedDataSetAt(e.Index));
134+
_previewPresenter.PlotDataRepository(dataRepository.DataRepository);
146135
}
147136

148137
public void SetSettings(IReadOnlyList<MetaDataCategory> metaDataCategories, ColumnInfoCache columnInfos,
@@ -250,7 +239,7 @@ private void loadSheets(IDataSourceFile dataSourceFile, IReadOnlyList<string> sh
250239
_dataSource.SetDataFormat(_columnMappingPresenter.GetDataFormat());
251240

252241
var errors = _dataSource.AddSheets(sheets, _columnInfos, filter);
253-
242+
254243
var dimensionDTOs = _dataSourceToDimensionSelectionDTOMapper.MapFrom(_dataSource, sheetNames);
255244

256245
var ambiguousDimensionDTOs = dimensionDTOs.Where(x => x.Dimensions.Count > 1).ToList();

0 commit comments

Comments
 (0)