Skip to content

Commit

Permalink
Merge pull request #485 from RocketPy-Team/bug/v1.1.1-docs-build
Browse files Browse the repository at this point in the history
BUG: Function breaks if a header is present in the csv file
  • Loading branch information
phmbressan authored Nov 27, 2023
2 parents 5ff3476 + cd9d10c commit 0ef9849
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 41 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,18 @@ straightforward as possible.

-

## [v1.1.2] - 2023-11-25

You can install this version by running `pip install rocketpy==1.1.2`

### Fixed

- BUG: Function breaks if a header is present in the csv file [#485](https://github.com/RocketPy-Team/RocketPy/pull/485)

## [v1.1.1] - 2023-11-23

You can install this version by running `pip install rocketpy==1.1.1`


### Added

- DOC: Added this changelog file [#472](https://github.com/RocketPy-Team/RocketPy/pull/472)
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
author = "RocketPy Team"

# The full version, including alpha/beta/rc tags
release = "1.1.1"
release = "1.1.2"


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/user/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ If you want to choose a specific version to guarantee compatibility, you may ins

.. code-block:: shell
pip install rocketpy==1.1.1
pip install rocketpy==1.1.2
Optional Installation Method: ``conda``
Expand Down
4 changes: 2 additions & 2 deletions docs/user/motors/liquidmotor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ Then we must first define the tanks:
fuel_gas = Fluid(name="ethanol_g", density=1.59)

# Define tanks geometry
tanks_shape = CylindricalTank(radius = 0.1, height = 1, spherical_caps = True)
tanks_shape = CylindricalTank(radius = 0.1, height = 1.2, spherical_caps = True)

# Define tanks
oxidizer_tank = MassFlowRateBasedTank(
name="oxidizer tank",
geometry=tanks_shape,
flux_time=5,
initial_liquid_mass=32,
initial_gas_mass=0.1,
initial_gas_mass=0.01,
liquid_mass_flow_rate_in=0,
liquid_mass_flow_rate_out=lambda t: 32 / 3 * exp(-0.25 * t),
gas_mass_flow_rate_in=0,
Expand Down
111 changes: 76 additions & 35 deletions rocketpy/mathutils/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,26 @@ def __init__(
Parameters
----------
source : function, scalar, ndarray, string
The actual function. If type is function, it will be called for
evaluation. If type is int or float, it will be treated as a
constant function. If ndarray, its points will be used for
interpolation. An ndarray should be as [(x0, y0, z0), (x1, y1, z1),
(x2, y2, z2), ...] where x0 and y0 are inputs and z0 is output. If
string, imports file named by the string and treats it as csv.
The file is converted into ndarray and should not have headers.
source : callable, scalar, ndarray, string, or Function
The data source to be used for the function:
- Callable: Called for evaluation with input values. Must have the
desired inputs as arguments and return a single output value.
Input order is important. Example: Python functions, classes, and
methods.
- int or float: Treated as a constant value function.
- ndarray: Used for interpolation. Format as [(x0, y0, z0),
(x1, y1, z1), ..., (xn, yn, zn)], where 'x' and 'y' are inputs,
and 'z' is the output.
- string: Path to a CSV file. The file is read and converted into an
ndarray. The file can optionally contain a single header line.
- Function: Copies the source of the provided Function object,
creating a new Function with adjusted inputs and outputs.
inputs : string, sequence of strings, optional
The name of the inputs of the function. Will be used for
representation and graphing (axis names). 'Scalar' is default.
Expand Down Expand Up @@ -74,6 +86,13 @@ def __init__(
Returns
-------
None
Notes
-----
(I) CSV files can optionally contain a single header line. If present,
the header is ignored during processing.
(II) Fields in CSV files may be enclosed in double quotes. If fields are
not quoted, double quotes should not appear inside them.
"""
# Set input and output
if inputs is None:
Expand Down Expand Up @@ -133,25 +152,42 @@ def set_outputs(self, outputs):
return self

def set_source(self, source):
"""Set the source which defines the output of the function giving a
certain input.
"""Sets the data source for the function, defining how the function
produces output from a given input.
Parameters
----------
source : function, scalar, ndarray, string, Function
The actual function. If type is function, it will be called for
evaluation. If type is int or float, it will be treated as a
constant function. If ndarray, its points will be used for
interpolation. An ndarray should be as [(x0, y0, z0), (x1, y1, z1),
(x2, y2, z2), ...] where x0 and y0 are inputs and z0 is output. If
string, imports file named by the string and treats it as csv.
The file is converted into ndarray and should not have headers.
If the source is a Function, its source will be copied and another
Function will be created following the new inputs and outputs.
source : callable, scalar, ndarray, string, or Function
The data source to be used for the function:
- Callable: Called for evaluation with input values. Must have the
desired inputs as arguments and return a single output value.
Input order is important. Example: Python functions, classes, and
methods.
- int or float: Treated as a constant value function.
- ndarray: Used for interpolation. Format as [(x0, y0, z0),
(x1, y1, z1), ..., (xn, yn, zn)], where 'x' and 'y' are inputs,
and 'z' is the output.
- string: Path to a CSV file. The file is read and converted into an
ndarray. The file can optionally contain a single header line.
- Function: Copies the source of the provided Function object,
creating a new Function with adjusted inputs and outputs.
Notes
-----
(I) CSV files can optionally contain a single header line. If present,
the header is ignored during processing.
(II) Fields in CSV files may be enclosed in double quotes. If fields are
not quoted, double quotes should not appear inside them.
Returns
-------
self : Function
Returns the Function instance.
"""
_ = self._check_user_input(
source,
Expand All @@ -165,20 +201,17 @@ def set_source(self, source):
source = source.get_source()
# Import CSV if source is a string or Path and convert values to ndarray
if isinstance(source, (str, Path)):
# Read file and check for headers
with open(source, mode="r") as f:
first_line = f.readline()
# If headers are found...
if first_line[0] in ['"', "'"]:
# Headers available
first_line = first_line.replace('"', " ").replace("'", " ")
first_line = first_line.split(" , ")
self.set_inputs(first_line[0])
self.set_outputs(first_line[1:])
source = np.loadtxt(source, delimiter=",", skiprows=1, dtype=float)
# if headers are not found
else:
source = np.loadtxt(source, delimiter=",", dtype=float)
with open(source, "r") as file:
try:
source = np.loadtxt(file, delimiter=",", dtype=float)
except ValueError:
# If an error occurs, headers are present
source = np.loadtxt(source, delimiter=",", dtype=float, skiprows=1)
except Exception as e:
raise ValueError(
"The source file is not a valid csv or txt file."
) from e

# Convert to ndarray if source is a list
if isinstance(source, (list, tuple)):
source = np.array(source, dtype=np.float64)
Expand Down Expand Up @@ -2830,7 +2863,15 @@ def _check_user_input(
# Deal with csv or txt
if isinstance(source, (str, Path)):
# Convert to numpy array
source = np.loadtxt(source, delimiter=",", dtype=float)
try:
source = np.loadtxt(source, delimiter=",", dtype=float)
except ValueError:
# Skip header
source = np.loadtxt(source, delimiter=",", dtype=float, skiprows=1)
except Exception as e:
raise ValueError(
"The source file is not a valid csv or txt file."
) from e

else:
# this will also trigger an error if the source is not a list of
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

setuptools.setup(
name="rocketpy",
version="1.1.1",
version="1.1.2",
install_requires=necessary_require,
extras_require={
"env_analysis": env_analysis_require,
Expand Down
4 changes: 4 additions & 0 deletions tests/fixtures/function/1d_no_quotes.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
time,value
0,100
1,200
2,300
4 changes: 4 additions & 0 deletions tests/fixtures/function/1d_quotes.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"time","value"
0,100
1,200
2,300
17 changes: 17 additions & 0 deletions tests/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ def test_function_from_csv(func_from_csv, func_2d_from_csv):
)


@pytest.mark.parametrize(
"csv_file",
[
"tests/fixtures/function/1d_quotes.csv",
"tests/fixtures/function/1d_no_quotes.csv",
],
)
def test_func_from_csv_with_header(csv_file):
"""Tests if a Function can be created from a CSV file with a single header
line. It tests cases where the fields are separated by quotes and without
quotes."""
f = Function(csv_file)
assert f.__repr__() == "'Function from R1 to R1 : (Scalar) → (Scalar)'"
assert np.isclose(f(0), 100)
assert np.isclose(f(0) + f(1), 300), "Error summing the values of the function"


def test_getters(func_from_csv, func_2d_from_csv):
"""Test the different getters of the Function class.
Expand Down

0 comments on commit 0ef9849

Please sign in to comment.