Skip to content

Commit

Permalink
Merge pull request #50 from lbl-srg/issue49_localTolerance
Browse files Browse the repository at this point in the history
Issue49 local tolerance
  • Loading branch information
AntoineGautier authored Mar 24, 2021
2 parents 9a61824 + d36837e commit 76b2248
Show file tree
Hide file tree
Showing 60 changed files with 1,491 additions and 442 deletions.
22 changes: 11 additions & 11 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ matrix:
env: ARCH_OPTION=""
compiler: gcc
python: 3.8
- name: Python 2.7 on macOS
os: osx
env: ARCH_OPTION=""
compiler: gcc
env: PYENV_VERSION=2.7
- name: Python 3.6 on macOS
os: osx
env: ARCH_OPTION=""
compiler: gcc
env: PYENV_VERSION=3.6
# - name: Python 2.7 on macOS
# os: osx
# env: ARCH_OPTION=""
# compiler: gcc
# env: PYENV_VERSION=2.7
# - name: Python 3.6 on macOS
# os: osx
# env: ARCH_OPTION=""
# compiler: gcc
# env: PYENV_VERSION=3.6
- name: Python 2.7 on Windows
os: windows
env: ARCH_OPTION="-A x64"
Expand Down Expand Up @@ -69,7 +69,7 @@ script:
# Compile, link, install.
- cmake --build . --target install --config Release
# Test and clean.
- ctest -C Release
- ctest -C Release --verbose
# Compare.
- git status tests
- git diff --exit-code tests
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Change Log

## Version 0.3.0

- Add local tolerance parameters `ltolx` and `ltoly`
- Allow for not specifying any tolerance parameter (defaults to 0)
- Normalize only in x direction when computing the L1 tolerance domains
- Fix tube size computation bugs when using `rtolx` and `rtoly`
22 changes: 5 additions & 17 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
# * CMake CMAKE_SYSTEM_PROCESSOR in tool chain file: error at build time
# see https://stackoverflow.com/questions/54151877/cmake-cmake-system-processor-in-tool-chain-file-error-at-build-time
#
# @todo
# Add test with Python 3
# Temporary test dir for parallel running

cmake_policy(SET CMP0048 NEW)
cmake_minimum_required(VERSION 3.11.0)
Expand All @@ -37,8 +34,7 @@ else()
endif()

# Set compiling and linking options.
# Consider adding -std=c89 -pedantic to gcc options to improve portability.
set(UNIX_COMPILE_FLAGS "-Wall -Wfloat-equal -o3 -fPIC")
set(UNIX_COMPILE_FLAGS "-Wall -Wextra -Werror -Wfloat-equal -Wpedantic -o3 -fPIC")
set(WIN_COMPILE_FLAGS "/Wall /O2")
set(UNIX_LINK_FLAGS "-fPIC")

Expand All @@ -50,7 +46,7 @@ else()
endif()

# Set target directories.
# NOTA: always add quotes to protect spaces in path when setting new variables.
# NOTE: always add quotes to protect spaces in path when setting new variables.
if (WINDOWS)
if(CMAKE_SYSTEM_PROCESSOR MATCHES ".*64$")
set(PLATFORM_INSTALL_PREFIX win64)
Expand Down Expand Up @@ -117,22 +113,14 @@ add_test(
WORKING_DIRECTORY "${CMAKE_TEST_DIR}/test_bin"
)
set_tests_properties(test_py_1 PROPERTIES PASS_REGULAR_EXPRESSION "No such file")
### Again
set(TEST_ARGS_2 --reference trended.csv --test simulated.csv --atoly 0.002 --output results)
### Wrong arguments
set(TEST_ARGS_2 --reference trended.csv --test simulated.csv --atolx 0.002 --atoly 0.002)
add_test(
NAME test_py_2
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_SOURCE_DIR}/pyfunnel/pyfunnel.py" ${TEST_ARGS_2}
WORKING_DIRECTORY "${CMAKE_TEST_DIR}/test_bin"
)
set_tests_properties(test_py_2 PROPERTIES PASS_REGULAR_EXPRESSION "At least one of the two possible tolerance parameters")
### Again
set(TEST_ARGS_3 --reference trended.csv --test simulated.csv --atolx 0.002 --atoly 0.002)
add_test(
NAME test_py_3
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_SOURCE_DIR}/pyfunnel/pyfunnel.py" ${TEST_ARGS_3}
WORKING_DIRECTORY "${CMAKE_TEST_DIR}/test_bin"
)
set_tests_properties(test_py_3 PROPERTIES PASS_REGULAR_EXPRESSION "Output directory not specified")
set_tests_properties(test_py_2 PROPERTIES PASS_REGULAR_EXPRESSION "Output directory not specified")

## Numerics testing.

Expand Down
60 changes: 33 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,33 +27,47 @@ than a simulation model that is considered to be the original specification.

### Method to Compute the Funnel

The funnel is computed as follows:
The funnel is computed as follows.

* First step: tolerance areas (based on L1-norm) are built around each reference data point.
1. Tolerance areas (based on L1-norm) are built around each reference data point.

* Second step: the algorithm selects which corners of the tolerance rectangles
are to be used to build the envelopes based on the change in the derivative sign at
* The tolerance parameters correspond to the half-width and half-height of the
tolerance areas. They default to 0.

* When using `atolx` and `atoly`, the tolerance is considered as absolute
(same unit as `x` and `y`).

* When using `ltolx` and `ltoly`, the tolerance is considered relative
to the local value of `x` and `y`.

* When using `rtolx` and `rtoly`, the tolerance is considered relative
to the range of `x` and `y`. This option is available mainly for compatibility with
the algorithm implemented in [csv-compare](https://github.com/modelica-tools/csv-compare)
for relative comparison. It should be used with caution.

2. The algorithm selects which corners of the tolerance rectangles
are used to build the envelopes based on the change in the derivative sign at
each reference point.

* Third step: intersection boundary points are computed when a selected corner
happens not to be in the logical order with the next one on the `x` scale (i.e., at a local extremum).
New envelopes are then built encompassing all boundary points, and points strictly within
the envelopes are dropped.
3. Intersection boundary points are computed when a selected corner
happens not to be in the logical order with the next one on the `x` scale
(i.e., at a local extremum).
New envelopes are then built encompassing all boundary points, and points strictly
within the envelopes are dropped.

The comparison then simply consists of interpolating the upper and lower envelopes
at the `x` test values and comparing the yielded `y_up` and `y_low` values with the `y` test values.
By convention, the error is `max(0, y - y_up) - min(0, y - y_low)` and hence it is always positive.


## How to Run

### System Requirements

The software has been tested on the following platforms.
The software is tested on the following platforms.

* Linux x64
* Windows x64
* Mac OS X
* macOS

A Python binding is available to access the library. It is compatible with Python 2 and 3.

Expand All @@ -73,18 +87,14 @@ The package `pyfunnel` provides the following functions.
* `compareAndReport`: calls `funnel` binary with list-like objects as `x`, `y` reference and test values.
Outputs `errors.csv`, `lowerBound.csv`, `upperBound.csv`, `reference.csv`, `test.csv`
into the output directory (`./results` by default).
Note: At least one absolute or relative tolerance parameter must be provided for each axis.
See function docstring for further details.

* `plot_funnel`: plots `funnel` results stored in the directory which path is provided as argument.
Displays plot in default browser. See function docstring for further details.

The module `pyfunnel.py` might also be called directly from terminal.
The module `pyfunnel.py` can also be run with the following command line interface.

```
usage: pyfunnel.py [-h] --reference REFERENCE --test TEST [--output OUTPUT]
[--atolx ATOLX] [--atoly ATOLY] [--rtolx RTOLX]
[--rtoly RTOLY]
usage: pyfunnel.py [-h] --reference REFERENCE --test TEST [--output OUTPUT] [--atolx ATOLX] [--atoly ATOLY] [--ltolx LTOLX] [--ltoly LTOLY] [--rtolx RTOLX] [--rtoly RTOLY]
Run funnel binary from terminal.
Expand All @@ -95,20 +105,16 @@ optional arguments:
--output OUTPUT Path of directory to store output data
--atolx ATOLX Absolute tolerance along x axis
--atoly ATOLY Absolute tolerance along y axis
--rtolx RTOLX Relative tolerance along x axis
--rtoly RTOLY Relative tolerance along y axis
--ltolx LTOLX Relative tolerance along x axis (relatively to the local value)
--ltoly LTOLY Relative tolerance along y axis (relatively to the local value)
--rtolx RTOLX Relative tolerance along x axis (relatively to the range)
--rtoly RTOLY Relative tolerance along y axis (relatively to the range)
required named arguments:
--reference REFERENCE
Path of CSV file with reference data
--test TEST Path of CSV file with test data
Note: At least one of the two possible tolerance parameters (atol or rtol) must be defined for each axis.
Relative tolerance is relative to the range of x or y values.
Typical use from terminal:
$ python {path to pyfunnel.py} --reference trended.csv --test simulated.csv --atolx 0.002 --atoly 0.002 --output results
Full documentation at https://github.com/lbl-srg/funnel
```

Expand Down Expand Up @@ -136,8 +142,8 @@ $ python ../../pyfunnel/pyfunnel.py --reference trended.csv --test simulated.csv

The cross-platform build system relies on CMake version `3.11.*`.

The distributed binaries have been built with Microsoft Visual Studio C/C++ compiler
(Windows) and `gcc` (Linux and Mac).
The distributed binaries are built with Microsoft Visual Studio C/C++ compiler
(Windows), Clang (macOS) and GCC (Linux).

### Procedure

Expand Down
2 changes: 1 addition & 1 deletion pyfunnel/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.2
0.3.0
22 changes: 11 additions & 11 deletions pyfunnel/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ def compareAndReport(
outputDirectory=None,
atolx=None,
atoly=None,
ltolx=None,
ltoly=None,
rtolx=None,
rtoly=None
):
Expand All @@ -201,24 +203,18 @@ def compareAndReport(
outputDirectory (str): path of directory to store output files
atolx (float): absolute tolerance along x axis
atoly (float): absolute tolerance along y axis
rtolx (float): relative tolerance along x axis
rtoly (float): relative tolerance along y axis
ltolx (float): relative tolerance along x axis (relatively to the local value)
ltoly (float): relative tolerance along y axis (relatively to the local value)
rtolx (float): relative tolerance along x axis (relatively to the range)
rtoly (float): relative tolerance along y axis (relatively to the range)
Returns:
None
Note: At least one absolute or relative tolerance parameter must be provided for each axis.
Relative tolerance is relative to the range of x or y values.
Full documentation at https://github.com/lbl-srg/funnel.
"""

# Check arguments.
# Logic
assert (atolx is not None) or (rtolx is not None),\
"At least one of the two possible tolerance parameters (atol or rtol) must be defined for x values."
assert (atoly is not None) or (rtoly is not None),\
"At least one of the two possible tolerance parameters (atol or rtol) must be defined for y values."
# Type
if outputDirectory is None:
print("Output directory not specified: results are stored in subdirectory `results` by default.")
Expand Down Expand Up @@ -251,7 +247,7 @@ def compareAndReport(
# Convert None tolerance to 0.
tol = dict()
args = locals()
for k in ('atolx', 'atoly', 'rtolx', 'rtoly'):
for k in ('atolx', 'atoly', 'ltolx', 'ltoly', 'rtolx', 'rtoly'):
if args[k] is None:
tol[k] = 0.0
else:
Expand Down Expand Up @@ -289,6 +285,8 @@ def compareAndReport(
c_double,
c_double,
c_double,
c_double,
c_double,
c_double]
lib.compareAndReport.restype = c_int

Expand All @@ -304,6 +302,8 @@ def compareAndReport(
outputDirectory,
tol['atolx'],
tol['atoly'],
tol['ltolx'],
tol['ltoly'],
tol['rtolx'],
tol['rtoly'],
)
Expand Down
Binary file modified pyfunnel/lib/darwin64/libfunnel.dylib
Binary file not shown.
Binary file modified pyfunnel/lib/linux64/libfunnel.so
Binary file not shown.
Binary file modified pyfunnel/lib/win64/funnel.dll
Binary file not shown.
Binary file modified pyfunnel/lib/win64/funnel.lib
Binary file not shown.
41 changes: 22 additions & 19 deletions pyfunnel/pyfunnel.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,19 @@
import textwrap
import sys

from core import compareAndReport
import core


if __name__ == "__main__":

# Configure the argument parser.
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent("""\
Run funnel binary from terminal.\n
Output `errors.csv`, `lowerBound.csv`, `upperBound.csv`, `reference.csv`, `test.csv` into the output directory (`./results` by default).
"""),
epilog=textwrap.dedent("""\
Note: At least one of the two possible tolerance parameters (atol or rtol) must be defined for each axis.
Relative tolerance is relative to the range of x or y values.\n
Typical use from terminal:
$ python {path to pyfunnel.py} --reference trended.csv --test simulated.csv --atolx 0.002 --atoly 0.002 --output results\n
Full documentation at https://github.com/lbl-srg/funnel
""")
description=(
'Run funnel binary from terminal.\n\n'
'Output `errors.csv`, `lowerBound.csv`, `upperBound.csv`, `reference.csv`, `test.csv` '
'into the output directory (`./results` by default).'),
epilog='Full documentation at https://github.com/lbl-srg/funnel'
)
required_named = parser.add_argument_group('required named arguments')

Expand Down Expand Up @@ -58,25 +53,31 @@
type=float,
help="Absolute tolerance along y axis"
)
parser.add_argument(
"--ltolx",
type=float,
help="Relative tolerance along x axis (relatively to the local value)"
)
parser.add_argument(
"--ltoly",
type=float,
help="Relative tolerance along y axis (relatively to the local value)"
)
parser.add_argument(
"--rtolx",
type=float,
help="Relative tolerance along x axis"
help="Relative tolerance along x axis (relatively to the range)"
)
parser.add_argument(
"--rtoly",
type=float,
help="Relative tolerance along y axis"
help="Relative tolerance along y axis (relatively to the range)"
)

# Parse the arguments.
args = parser.parse_args()

# Check the arguments.
assert (args.atolx is not None) or (args.rtolx is not None),\
"At least one of the two possible tolerance parameters (atol or rtol) must be defined for x values."
assert (args.atoly is not None) or (args.rtoly is not None),\
"At least one of the two possible tolerance parameters (atol or rtol) must be defined for y values."
assert os.path.isfile(args.reference),\
"No such file: {}".format(args.reference)
assert os.path.isfile(args.test),\
Expand All @@ -96,14 +97,16 @@
pass

# Call the function.
rc = compareAndReport(
rc = core.compareAndReport(
xReference=data['reference']['x'],
yReference=data['reference']['y'],
xTest=data['test']['x'],
yTest=data['test']['y'],
outputDirectory=args.output,
atolx=args.atolx,
atoly=args.atoly,
ltolx=args.ltolx,
ltoly=args.ltoly,
rtolx=args.rtolx,
rtoly=args.rtoly,
)
Expand Down
Loading

0 comments on commit 76b2248

Please sign in to comment.