From 2cfbda355e6b416b3405429cb40bf7567d2d79b6 Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Wed, 15 Nov 2023 14:10:17 -0300 Subject: [PATCH 01/10] DOC: start Function documentation page (WIP). --- docs/user/function.rst | 104 +++++++++++++++++++++++++++++++++++++++++ docs/user/index.rst | 1 + 2 files changed, 105 insertions(+) create mode 100644 docs/user/function.rst diff --git a/docs/user/function.rst b/docs/user/function.rst new file mode 100644 index 000000000..2387dead9 --- /dev/null +++ b/docs/user/function.rst @@ -0,0 +1,104 @@ +.. _functionusage:: + +Function Class Usage +==================== + +The ``rocketpy.Function`` class in RocketPy is an auxiliary module that allows for easy manipulation of datasets and Python functions. Some of the class features are data interpolation, extrapolation, algebra and plotting. + +The basic steps to create a ``Function`` are as follows: + +1. Define a data source: a dataset (e.g. x,y coordinates) or a function that maps a dataset to another (e.g. f(x) = x^2); +2. Construct a ``Function`` object with this dataset as ``source``; +3. Use the ``Function`` features as needed: add datasets, integrate at a point, make a scatter plot and much more. + +These basic steps are detailed in this guide. + +1. Define a Data Source +----------------------- + +The ``Function`` class supports a wide variety of data sources: + +Datasets +~~~~~~~~ + +- ``list`` or ``numpy.ndarray``: a list of datapoints that maps input values to an output. For instance, we can define a dataset that follows the function f(x) = x^2: + +.. jupyter-execute:: + from rocketpy import Function + + # Source dataset [(x0, y0), (x1, y1), ...] + source = [ + (-3, 9), (-2, 4), (-1, 1), + (0, 0), (1, 1), (2, 4), + (3, 9) + ] + + # Create a Function object with this dataset + f = Function(source) + + # Print the source to see the dataset + print(f.source) + + # Plot the source with standard spline interpolation + f.plot() + +The dataset can be defined as a higher dimensional array (more than one input maps to an output), where each row is a datapoint. For example, let us define a dataset that follows the plane z = x + y: + +.. jupyter-execute:: + # Source dataset [(x0, y0, z0), (x1, y1, z1), ...] + source = [ + (-1, -1, -2), (-1, 0, -1), (-1, 1, 0), + (0, -1, -1), (0, 0, 0), (0, 1, 1), + (1, -1, 0), (1, 0, 1), (1, 1, 2) + ] + + # Create a Function object with this dataset + f = Function(source) + + # Print the source to see the dataset + print(f.source) + + # Plot the source with standard 2d shepard interpolation + f.plot() + +.. important:: + The ``Function`` class only supports interpolation and extrapolation of type ``shepard`` for datasets higher than one dimension (more than one input). + +- ``string``: a csv file path that contains a dataset structured so that each line is a datapoint: the last column is the output and the previous columns are the inputs; + +.. jupyter-execute:: + # Create a csv and save with pandas + import pandas as pd + + df = pd.DataFrame({ + 'x': [-3, -2, -1, 0, 1, 2, 3], + 'y': [9, 4, 1, 0, 1, 4, 9], + }) + df.to_csv('source.csv', index=False) + #pd.read_csv('source.csv') + +Having the csv file, we can define a ``Function`` object with it: + +.. jupyter-execute:: + # Create a Function object with this dataset + #f = Function('source.csv') + + # One may even delete the csv file + #import os + #os.remove('source.csv') + + # Print the source to see the dataset + #print(f.source) + +.. note:: + A header in the csv file is optional, but if present must be in a string like format, i.e. beginning and ending with quotation marks. + + +.. note:: + The ``Function`` class plots only supports one or two dimensional inputs. + + + + + + diff --git a/docs/user/index.rst b/docs/user/index.rst index a5ff36bb8..ff06a8821 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -16,6 +16,7 @@ RocketPy's User Guide Motors Rocket Environment + Function .. toctree:: :maxdepth: 2 From 06342900461dd3f96a5b09740a687f1e5e11ea8d Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Wed, 15 Nov 2023 14:37:37 -0300 Subject: [PATCH 02/10] BUG: fix extrapolation of multivariable functions. --- rocketpy/mathutils/function.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/rocketpy/mathutils/function.py b/rocketpy/mathutils/function.py index 1fd878b36..4a879bf1f 100644 --- a/rocketpy/mathutils/function.py +++ b/rocketpy/mathutils/function.py @@ -231,8 +231,29 @@ def source(x): # Finally set data source as source self.source = source - if self.__interpolation__ is None: + + # Update extrapolation method + if ( + self.__extrapolation__ is None + or self.__extrapolation__ == "shepard" + ): + self.set_extrapolation("shepard") + else: + raise ValueError( + "Multidimensional datasets only support shepard extrapolation." + ) + + # Set default multidimensional interpolation if it hasn't + if ( + self.__interpolation__ is None + or self.__interpolation__ == "shepard" + ): self.set_interpolation("shepard") + else: + raise ValueError( + "Multidimensional datasets only support shepard interpolation." + ) + # Return self return self From 36d91c2d9af25b866db3eb9f43b559e65064535f Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Wed, 15 Nov 2023 15:02:30 -0300 Subject: [PATCH 03/10] TST: add tests for multivariable functions. --- rocketpy/mathutils/function.py | 6 +-- tests/test_function.py | 70 ++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/rocketpy/mathutils/function.py b/rocketpy/mathutils/function.py index 4a879bf1f..27fd8b717 100644 --- a/rocketpy/mathutils/function.py +++ b/rocketpy/mathutils/function.py @@ -235,12 +235,12 @@ def source(x): # Update extrapolation method if ( self.__extrapolation__ is None - or self.__extrapolation__ == "shepard" + or self.__extrapolation__ == "natural" ): - self.set_extrapolation("shepard") + self.set_extrapolation("natural") else: raise ValueError( - "Multidimensional datasets only support shepard extrapolation." + "Multidimensional datasets only support natural extrapolation." ) # Set default multidimensional interpolation if it hasn't diff --git a/tests/test_function.py b/tests/test_function.py index 4cbac9c33..82d38c1e5 100644 --- a/tests/test_function.py +++ b/tests/test_function.py @@ -173,3 +173,73 @@ def test_integral_spline_interpolation(request, func, a, b): func.integral(a, b, numerical=True), atol=1e-3, ) + + +@pytest.mark.parametrize("a", [-1, 0, 1]) +@pytest.mark.parametrize("b", [-1, 0, 1]) +def test_multivariable_dataset(a, b): + """Test the Function class with a multivariable dataset.""" + # Test plane f(x,y) = x + y + source = [ + (-1, -1, -2), + (-1, 0, -1), + (-1, 1, 0), + (0, -1, -1), + (0, 0, 0), + (0, 1, 1), + (1, -1, 0), + (1, 0, 1), + (1, 1, 2), + ] + func = Function(source=source, inputs=["x", "y"], outputs=["z"]) + + # Assert interpolation and extrapolation methods + assert func.get_interpolation_method() == "shepard" + assert func.get_extrapolation_method() == "natural" + + # Assert values + assert np.isclose(func(a, b), a + b, atol=1e-6) + + +@pytest.mark.parametrize("a", [-1, -0.5, 0, 0.5, 1]) +@pytest.mark.parametrize("b", [-1, -0.5, 0, 0.5, 1]) +def test_multivariable_function(a, b): + """Test the Function class with a multivariable function.""" + # Test plane f(x,y) = sin(x + y) + source = lambda x, y: np.sin(x + y) + func = Function(source=source, inputs=["x", "y"], outputs=["z"]) + + # Assert values + assert np.isclose(func(a, b), np.sin(a + b), atol=1e-6) + + +@patch("matplotlib.pyplot.show") +def test_multivariable_dataset_plot(mock_show): + """Test the plot method of the Function class with a multivariable dataset.""" + # Test plane f(x,y) = x - y + source = [ + (-1, -1, -1), + (-1, 0, -1), + (-1, 1, -2), + (0, 1, 1), + (0, 0, 0), + (0, 1, -1), + (1, -1, 2), + (1, 0, 1), + (1, 1, 0), + ] + func = Function(source=source, inputs=["x", "y"], outputs=["z"]) + + # Assert plot + assert func.plot() == None + + +@patch("matplotlib.pyplot.show") +def test_multivariable_function_plot(mock_show): + """Test the plot method of the Function class with a multivariable function.""" + # Test plane f(x,y) = sin(x + y) + source = lambda x, y: np.sin(x * y) + func = Function(source=source, inputs=["x", "y"], outputs=["z"]) + + # Assert plot + assert func.plot() == None From 4b79765857130d731df87b869e26c7dfa5ebdf1d Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Thu, 16 Nov 2023 09:09:27 -0300 Subject: [PATCH 04/10] DOC: improve Function callable source docs. --- docs/user/function.rst | 123 ++++++++++++++++++++++++++++++++++------- docs/user/index.rst | 2 +- 2 files changed, 104 insertions(+), 21 deletions(-) diff --git a/docs/user/function.rst b/docs/user/function.rst index 2387dead9..4a8802ca3 100644 --- a/docs/user/function.rst +++ b/docs/user/function.rst @@ -3,11 +3,11 @@ Function Class Usage ==================== -The ``rocketpy.Function`` class in RocketPy is an auxiliary module that allows for easy manipulation of datasets and Python functions. Some of the class features are data interpolation, extrapolation, algebra and plotting. +The :class:`rocketpy.mathutils.Function` class in RocketPy is an auxiliary module that allows for easy manipulation of datasets and Python functions. Some of the class features are data interpolation, extrapolation, algebra and plotting. The basic steps to create a ``Function`` are as follows: -1. Define a data source: a dataset (e.g. x,y coordinates) or a function that maps a dataset to another (e.g. f(x) = x^2); +1. Define a data source: a dataset (e.g. x,y coordinates) or a function that maps a dataset to a value (e.g. f(x) = x^2); 2. Construct a ``Function`` object with this dataset as ``source``; 3. Use the ``Function`` features as needed: add datasets, integrate at a point, make a scatter plot and much more. @@ -16,15 +16,19 @@ These basic steps are detailed in this guide. 1. Define a Data Source ----------------------- -The ``Function`` class supports a wide variety of data sources: - Datasets ~~~~~~~~ -- ``list`` or ``numpy.ndarray``: a list of datapoints that maps input values to an output. For instance, we can define a dataset that follows the function f(x) = x^2: +The ``Function`` class supports a wide variety of dataset sources: + +List or Numpy Array +^^^^^^^^^^^^^^^^^^^ + +A ``list`` or ``numpy.ndarray`` of datapoints that maps input values to an output can be used as a ``Function`` source. For instance, we can define a dataset that follows the function f(x) = x^2: .. jupyter-execute:: - from rocketpy import Function + + from rocketpy.mathutils import Function # Source dataset [(x0, y0), (x1, y1), ...] source = [ @@ -34,17 +38,28 @@ Datasets ] # Create a Function object with this dataset - f = Function(source) + f = Function(source, "x", "y") + +One may print the source attribute from the ``Function`` object to check the inputed dataset. + +.. jupyter-execute:: # Print the source to see the dataset print(f.source) +Furthermore, in order to visualize the dataset, one may use the ``plot`` method from the ``Function`` object: + +.. jupyter-execute:: + # Plot the source with standard spline interpolation f.plot() -The dataset can be defined as a higher dimensional array (more than one input maps to an output), where each row is a datapoint. For example, let us define a dataset that follows the plane z = x + y: +| + +The dataset can be defined as a *multidimensional* array (more than one input maps to an output), where each row is a datapoint. For example, let us define a dataset that follows the plane z = x + y: .. jupyter-execute:: + # Source dataset [(x0, y0, z0), (x1, y1, z1), ...] source = [ (-1, -1, -2), (-1, 0, -1), (-1, 1, 0), @@ -53,52 +68,120 @@ The dataset can be defined as a higher dimensional array (more than one input ma ] # Create a Function object with this dataset - f = Function(source) + f = Function(source, ["x", "y"], "z") + +One may print the source attribute from the ``Function`` object to check the inputed dataset. + +.. jupyter-execute:: - # Print the source to see the dataset print(f.source) +Two dimensional plots are also supported, therefore this datasource can be plotted as follows: + +.. jupyter-execute:: + # Plot the source with standard 2d shepard interpolation f.plot() .. important:: - The ``Function`` class only supports interpolation and extrapolation of type ``shepard`` for datasets higher than one dimension (more than one input). + The ``Function`` class only supports interpolation ``shepard`` and extrapolation ``natural`` for datasets higher than one dimension (more than one input). + +CSV File +^^^^^^^^ -- ``string``: a csv file path that contains a dataset structured so that each line is a datapoint: the last column is the output and the previous columns are the inputs; +A CSV file path can be passed as ``string`` to the ``Function`` source. The file must contain a dataset structured so that each line is a datapoint: the last column is the output and the previous columns are the inputs. .. jupyter-execute:: + # Create a csv and save with pandas import pandas as pd df = pd.DataFrame({ - 'x': [-3, -2, -1, 0, 1, 2, 3], - 'y': [9, 4, 1, 0, 1, 4, 9], + '"x"': [-3, -2, -1, 0, 1, 2, 3], + '"y"': [9, 4, 1, 0, 1, 4, 9], }) df.to_csv('source.csv', index=False) - #pd.read_csv('source.csv') + pd.read_csv('source.csv') + +| Having the csv file, we can define a ``Function`` object with it: .. jupyter-execute:: + # Create a Function object with this dataset - #f = Function('source.csv') + f = Function('source.csv') # One may even delete the csv file - #import os - #os.remove('source.csv') + import os + os.remove('source.csv') # Print the source to see the dataset - #print(f.source) + print(f.source) .. note:: A header in the csv file is optional, but if present must be in a string like format, i.e. beginning and ending with quotation marks. +Function Map +~~~~~~~~~~~~ + +A Python function that maps a set of parameters to a result can be used as a ``Function`` source. For instance, we can define a function that maps x to f(x) = sin(x): + +.. jupyter-execute:: + + import numpy as np + from rocketpy.mathutils import Function + + # Define source function + def source_func(x): + return np.sin(x) + + # Create a Function from source + f = Function(source_func) + +The result of this operation is a ``Function`` object that wraps the source function and features many functionalities, such as plotting. + +Constant Functions +^^^^^^^^^^^^^^^^^^ + +A special case of the python function source is the definition of a constant ``Function``. The class supports a convenient shortcut to ease the definition of a constant source: + +.. jupyter-execute:: + + # Constant function + f = Function(1.5) + + print(f(0)) + .. note:: - The ``Function`` class plots only supports one or two dimensional inputs. + This shortcut is completely equivalent to defining a Python constant function as the source: + + .. jupyter-input:: + def const_source(_): + return 1.5 + g = Function(const_source) +2. Building your Function +------------------------- +In this section we are going to delve deeper on ``Function`` creation and its parameters: +- source: the ``Function`` datasource. We have explored this parameter in the section above; +- inputs: a list of strings containing each input variable name. If the source only has one input, may be abbreviated as a string (e.g. "speed (m/s)"); +- outputs: a list of strings containing each output variable name. If the source only has one output, may be abbreviated as a string (e.g. "total energy (J)"); +- interpolation: a string that is the interpolation method to be used if the source is a dataset. Defaults to ``spline``; +- extrapolation: a string that is the extrapolation method to be used if the source is a dataset. Defaults to ``constant``; +- title: the title to be shown in the plots. +With these in mind, let us create a more concrete example so that each of these parameters usefulness is explored.:: + + Suppose we have a particle named Bob + +.. seealso:: + Check out more about the constructor parameters and other functionalities in the :class:`rocketpy.mathutils.Function` documentation. + +.. note:: + The ``Function`` class plots only supports one or two dimensional inputs. diff --git a/docs/user/index.rst b/docs/user/index.rst index ff06a8821..cc7591631 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -16,7 +16,6 @@ RocketPy's User Guide Motors Rocket Environment - Function .. toctree:: :maxdepth: 2 @@ -37,4 +36,5 @@ RocketPy's User Guide :maxdepth: 2 :caption: Further Analysis + Function Utilities \ No newline at end of file From ba0a5132c2d7db3659801dbdfa1fb75f11c47373 Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Fri, 17 Nov 2023 13:16:47 -0300 Subject: [PATCH 05/10] DOC: detail Function features and computations. --- docs/user/function.rst | 242 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 220 insertions(+), 22 deletions(-) diff --git a/docs/user/function.rst b/docs/user/function.rst index 4a8802ca3..5f849eba9 100644 --- a/docs/user/function.rst +++ b/docs/user/function.rst @@ -3,11 +3,11 @@ Function Class Usage ==================== -The :class:`rocketpy.mathutils.Function` class in RocketPy is an auxiliary module that allows for easy manipulation of datasets and Python functions. Some of the class features are data interpolation, extrapolation, algebra and plotting. +The :class:`rocketpy.Function` class in RocketPy is an auxiliary module that allows for easy manipulation of datasets and Python functions. Some of the class features are data interpolation, extrapolation, algebra and plotting. The basic steps to create a ``Function`` are as follows: -1. Define a data source: a dataset (e.g. x,y coordinates) or a function that maps a dataset to a value (e.g. f(x) = x^2); +1. Define a data source: a dataset (e.g. x,y coordinates) or a function that maps a dataset to a value (e.g. :math:`f(x) = x^2`); 2. Construct a ``Function`` object with this dataset as ``source``; 3. Use the ``Function`` features as needed: add datasets, integrate at a point, make a scatter plot and much more. @@ -16,15 +16,15 @@ These basic steps are detailed in this guide. 1. Define a Data Source ----------------------- -Datasets -~~~~~~~~ +a. Datasets +~~~~~~~~~~~ The ``Function`` class supports a wide variety of dataset sources: -List or Numpy Array -^^^^^^^^^^^^^^^^^^^ +- List or Numpy Array +^^^^^^^^^^^^^^^^^^^^^ -A ``list`` or ``numpy.ndarray`` of datapoints that maps input values to an output can be used as a ``Function`` source. For instance, we can define a dataset that follows the function f(x) = x^2: +A ``list`` or ``numpy.ndarray`` of datapoints that maps input values to an output can be used as a ``Function`` source. For instance, we can define a dataset that follows the function :math:`f(x) = x^2`: .. jupyter-execute:: @@ -56,7 +56,7 @@ Furthermore, in order to visualize the dataset, one may use the ``plot`` method | -The dataset can be defined as a *multidimensional* array (more than one input maps to an output), where each row is a datapoint. For example, let us define a dataset that follows the plane z = x + y: +The dataset can be defined as a *multidimensional* array (more than one input maps to an output), where each row is a datapoint. For example, let us define a dataset that follows the plane :math:`z = x + y`: .. jupyter-execute:: @@ -70,13 +70,13 @@ The dataset can be defined as a *multidimensional* array (more than one input ma # Create a Function object with this dataset f = Function(source, ["x", "y"], "z") -One may print the source attribute from the ``Function`` object to check the inputed dataset. +One may print the source attribute from the ``Function`` object to check the input dataset. .. jupyter-execute:: print(f.source) -Two dimensional plots are also supported, therefore this datasource can be plotted as follows: +Two dimensional plots are also supported, therefore this data source can be plotted as follows: .. jupyter-execute:: @@ -86,8 +86,8 @@ Two dimensional plots are also supported, therefore this datasource can be plott .. important:: The ``Function`` class only supports interpolation ``shepard`` and extrapolation ``natural`` for datasets higher than one dimension (more than one input). -CSV File -^^^^^^^^ +- CSV File +^^^^^^^^^^ A CSV file path can be passed as ``string`` to the ``Function`` source. The file must contain a dataset structured so that each line is a datapoint: the last column is the output and the previous columns are the inputs. @@ -122,10 +122,10 @@ Having the csv file, we can define a ``Function`` object with it: .. note:: A header in the csv file is optional, but if present must be in a string like format, i.e. beginning and ending with quotation marks. -Function Map -~~~~~~~~~~~~ +b. Function Map +~~~~~~~~~~~~~~~ -A Python function that maps a set of parameters to a result can be used as a ``Function`` source. For instance, we can define a function that maps x to f(x) = sin(x): +A Python function that maps a set of parameters to a result can be used as a ``Function`` source. For instance, we can define a function that maps x to :math:`f(x) = \sin(x)`: .. jupyter-execute:: @@ -141,8 +141,8 @@ A Python function that maps a set of parameters to a result can be used as a ``F The result of this operation is a ``Function`` object that wraps the source function and features many functionalities, such as plotting. -Constant Functions -^^^^^^^^^^^^^^^^^^ +- Constant Functions +^^^^^^^^^^^^^^^^^^^^ A special case of the python function source is the definition of a constant ``Function``. The class supports a convenient shortcut to ease the definition of a constant source: @@ -158,6 +158,7 @@ A special case of the python function source is the definition of a constant ``F This shortcut is completely equivalent to defining a Python constant function as the source: .. jupyter-input:: + def const_source(_): return 1.5 @@ -176,12 +177,209 @@ In this section we are going to delve deeper on ``Function`` creation and its pa - extrapolation: a string that is the extrapolation method to be used if the source is a dataset. Defaults to ``constant``; - title: the title to be shown in the plots. -With these in mind, let us create a more concrete example so that each of these parameters usefulness is explored.:: +.. seealso:: + Check out more about the constructor parameters and other functionalities in the :class:`rocketpy.Function` documentation. - Suppose we have a particle named Bob +With these in mind, let us create a more concrete example so that each of these parameters usefulness is explored. -.. seealso:: - Check out more about the constructor parameters and other functionalities in the :class:`rocketpy.mathutils.Function` documentation. + Suppose we have a dataset containing the data from a static fire test of a rocket engine in testing phase. The dataset contain has a column for time (s) and thrust (N). We want to create a ``Function`` object that represents the thrust curve of this engine. + +.. jupyter-execute:: + + from rocketpy.mathutils import Function + + # Static fire data + motor_thrust = [ + (0, 0), (0.5, 1500), (1, 2000), + (1.5, 2100), (2, 1900), (2.5, 800), + (3, 0) + ] + + # Create a Function object with this dataset + thrust = Function( + source=motor_thrust, + inputs="time (s)", + outputs="thrust (N)", + interpolation="spline", + extrapolation="zero", + title="Static Fire Thrust Curve" + ) + +The parameters ``interpolation`` and ``extrapolation`` are of particular importance in this example: + +- Due the fact the data is quite sparse, we want to use a ``spline`` interpolation to smooth the curve. +- The extrapolation method is set to ``zero`` because we know that the thrust is zero before and after the test. + +Let's plot this curve to visualize the effect of these options in action: + +.. jupyter-execute:: + + # Plotting from 0 to 5 seconds + thrust.plot(0, 5) .. note:: - The ``Function`` class plots only supports one or two dimensional inputs. + Compare the interpolation and extrapolation effects by changing their methods. Check out this plot results with ``linear`` interpolation and ``constant`` extrapolation and see their difference. + +3. Function Features +-------------------- + +The ``Function`` class has many features that can be used to manipulate the source data. In this section we are going to explore some of these features, such as Function call, Function arithmetic, discretization, differentiation and integration. + +a. Function Call +~~~~~~~~~~~~~~~~ + +A ``Function`` objects maps input data to an output, therefore should you want to get an output value from a given input, this can be accomplished by the method :meth:`rocketpy.Function.get_value`: + +.. jupyter-execute:: + + from rocketpy.mathutils import Function + + f = Function(lambda x: x**0.5) + + print(f.get_value(9)) + +Equivalently, the same operation is defined by the Python dunder method ``__call__`` so that the object can be used like a common function. For instance: + +.. jupyter-execute:: + + print(f(9), f(25)) + +Furthermore, the :meth:`rocketpy.Function.get_value` method can be used to get a list of outputs from a list of inputs: + +.. jupyter-execute:: + + print(f.get_value([1, 4, 9, 16, 25])) + +b. Function Arithmetic +~~~~~~~~~~~~~~~~~~~~~~ + +An important feature of the ``Function`` class is the ability to perform arithmetic operations between real values or even other ``Function`` objects. + +.. jupyter-execute:: + + import numpy as np + + f = Function(lambda x: np.sin(x)) + + g = f/4 + 1 + + Function.compare_plots([f, g], lower=0, upper=4*np.pi) + +.. note:: + This is an example of the static method :meth:`rocketpy.Function.compare_plots`, it is used to plot Functions in the same graph for comparison. + +Arithmetic can also be performed on sets of data of the same length and same domain discretization (i.e. equal x values): + +.. jupyter-execute:: + + source1 = [(0, 0), (0.5, 0.25), (1, 1), (1.5, 2.25), (2, 4)] + source2 = [(0, 0), (0.5, 0.5), (1, 1), (1.5, 1.5), (2, 2)] + + f = Function(source1) + g = Function(source2) + + h = (f + g) / 2 + + Function.compare_plots([f, g, h], lower=0, upper=2) + +c. Discretization +~~~~~~~~~~~~~~~~~ + +The ``Function`` class can also convert from function sourced to a discretized dataset produced from it. This is accomplished by the method :meth:`rocketpy.Function.set_discrete` and allows for a great computational speed up if the function source is complex. + +The accuracy of the discretization depends on the number of datapoints and the chosen interpolation method. + +Let's compare the discretization of a sine function: + +.. jupyter-execute:: + + import numpy as np + from copy import copy + + # Function from sine + f = Function(lambda x: np.sin(x)) + + # Discretization + f_continuous = copy(f) + f_discrete = f.set_discrete( + lower=0, + upper=4*np.pi, + samples=20, + interpolation="linear" + ) + + Function.compare_plots([f_continuous, f_discrete], lower=0, upper=4*np.pi) + +.. important:: + + A `copy` of the original continuous function was necessary in this example, since the method :meth:`rocketpy.Function.set_discrete` mutates the original ``Function``. + +d. Differentiation and Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +One of the most useful ``Function`` features for data analysis is easily differentiating and integrating the data source. These methods are divided as follow: + +- :meth:`rocketpy.Function.differentiate`: differentiate the ``Function`` at a given point, returning the derivative value as the result; +- :meth:`rocketpy.Function.integral`: performs a definite integral over specified limits, returns the integral value (area under ``Function``); +- :meth:`rocketpy.Function.derivative_function`: computes the derivative of the given `Function`, returning another `Function` that is the derivative of the original at each point; +- :meth:`rocketpy.Function.integral_function`: calculates the definite integral of the function from a given point up to a variable, returns a ``Function``. + +Derivatives +^^^^^^^^^^^ + +Let's make a familiar example of differentiation: the derivative of the function :math:`f(x) = x^2` is :math:`f'(x) = 2x`. We can use the ``Function`` class to compute those: + +.. jupyter-execute:: + + # Define the function x^2 + f = Function(lambda x: x**2) + + # Differentiate it at x = 3 + print(f.differentiate(3)) + +Also one may compute the derivative function: + +.. jupyter-execute:: + + # Define the function x^2 and its derivative + f = Function(lambda x: x**2) + f_dot = f.derivative_function() + + # Compare their plots + Function.compare_plots([f, f_dot], lower=-2, upper=2) + +Integrals +^^^^^^^^^ + +Now, to illustrate the power of the ``Function`` class in making it easy to make plots of complex functions, let's plot the integral of the gaussian function: + +.. math:: + + f(x) = \frac{1}{\sqrt{2\pi}} \cdot e^{-\frac{x^2}{2}} + +Which is non-elementary so it cannot be expressed in terms of common functions. + +.. jupyter-execute:: + + # Define the gaussian function + def gaussian(x): + return 1 / np.sqrt(2*np.pi) * np.exp(-x**2/2) + + f = Function(gaussian) + + # Integrate from 0 to 1 + print(f.integral(0,1)) + +Here we have shown that we can integrate the gaussian function over a defined interval, let's compute its integral function. + +.. jupyter-execute:: + + # Compute the integral function from -4 + f_int = f.integral_function(-4, 4, 1000) + + # Compare the function with the integral + Function.compare_plots([f, f_int], lower=-4, upper=4) + +........ + +This guide shows some of the capabilities of the ``Function`` class, but there are many other functionalities to enhance your analysis. Do not hesitate in tanking a look at the documentation :class:`rocketpy.Function`. From e8191c293facb0cff6a92cd8ba9c262182f7de31 Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Fri, 17 Nov 2023 19:33:55 -0300 Subject: [PATCH 06/10] DOC: add pandas to docs requirements. --- docs/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 247c75a94..7d45029bb 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,4 +3,5 @@ pydata-sphinx-theme==0.13.3 m2r2>=0.2.1 jupyter-sphinx==0.4.0 sphinx_design==0.5.0 -ipykernel>=6.25.0 \ No newline at end of file +ipykernel>=6.25.0 +pandas>=1.4.4 \ No newline at end of file From 41cee0ab19ce66a764dc7cf83173917f3b88e365 Mon Sep 17 00:00:00 2001 From: Pedro Bressan Date: Fri, 17 Nov 2023 19:35:06 -0300 Subject: [PATCH 07/10] DOC: fix rst structure warnings. --- docs/user/function.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/user/function.rst b/docs/user/function.rst index 5f849eba9..58713bbff 100644 --- a/docs/user/function.rst +++ b/docs/user/function.rst @@ -1,4 +1,4 @@ -.. _functionusage:: +.. _functionusage: Function Class Usage ==================== @@ -21,8 +21,8 @@ a. Datasets The ``Function`` class supports a wide variety of dataset sources: -- List or Numpy Array -^^^^^^^^^^^^^^^^^^^^^ +List or Numpy Array +^^^^^^^^^^^^^^^^^^^ A ``list`` or ``numpy.ndarray`` of datapoints that maps input values to an output can be used as a ``Function`` source. For instance, we can define a dataset that follows the function :math:`f(x) = x^2`: @@ -86,8 +86,8 @@ Two dimensional plots are also supported, therefore this data source can be plot .. important:: The ``Function`` class only supports interpolation ``shepard`` and extrapolation ``natural`` for datasets higher than one dimension (more than one input). -- CSV File -^^^^^^^^^^ +CSV File +^^^^^^^^ A CSV file path can be passed as ``string`` to the ``Function`` source. The file must contain a dataset structured so that each line is a datapoint: the last column is the output and the previous columns are the inputs. @@ -141,8 +141,8 @@ A Python function that maps a set of parameters to a result can be used as a ``F The result of this operation is a ``Function`` object that wraps the source function and features many functionalities, such as plotting. -- Constant Functions -^^^^^^^^^^^^^^^^^^^^ +Constant Functions +^^^^^^^^^^^^^^^^^^ A special case of the python function source is the definition of a constant ``Function``. The class supports a convenient shortcut to ease the definition of a constant source: From b76127e7a26d7e4b582776fe837f9dcd06ad635e Mon Sep 17 00:00:00 2001 From: MateusStano <69485049+MateusStano@users.noreply.github.com> Date: Sat, 18 Nov 2023 16:47:49 -0300 Subject: [PATCH 08/10] Update docs/user/function.rst --- docs/user/function.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/function.rst b/docs/user/function.rst index 58713bbff..7400dfad0 100644 --- a/docs/user/function.rst +++ b/docs/user/function.rst @@ -182,7 +182,7 @@ In this section we are going to delve deeper on ``Function`` creation and its pa With these in mind, let us create a more concrete example so that each of these parameters usefulness is explored. - Suppose we have a dataset containing the data from a static fire test of a rocket engine in testing phase. The dataset contain has a column for time (s) and thrust (N). We want to create a ``Function`` object that represents the thrust curve of this engine. +Suppose we have a dataset containing the data from a static fire test of a rocket engine in testing phase. The dataset contain has a column for time (s) and thrust (N). We want to create a ``Function`` object that represents the thrust curve of this engine. .. jupyter-execute:: From 31fa95ea88ffc2021ef5093c68a00b7d141d97da Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sat, 18 Nov 2023 20:55:48 +0100 Subject: [PATCH 09/10] DOCS: add changes requested in review --- docs/user/function.rst | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/docs/user/function.rst b/docs/user/function.rst index 7400dfad0..8699946ee 100644 --- a/docs/user/function.rst +++ b/docs/user/function.rst @@ -217,8 +217,17 @@ Let's plot this curve to visualize the effect of these options in action: # Plotting from 0 to 5 seconds thrust.plot(0, 5) -.. note:: - Compare the interpolation and extrapolation effects by changing their methods. Check out this plot results with ``linear`` interpolation and ``constant`` extrapolation and see their difference. +Now lets visualize what happens if we were to use a ``linear`` interpolation and a ``constant`` extrapolation: + +.. jupyter-execute:: + + # Change interpolation and extrapolation + thrust.set_interpolation("linear") + thrust.set_extrapolation("constant") + + # Plotting from 0 to 5 seconds + thrust.plot(0, 5) + 3. Function Features -------------------- @@ -238,22 +247,37 @@ A ``Function`` objects maps input data to an output, therefore should you want t print(f.get_value(9)) -Equivalently, the same operation is defined by the Python dunder method ``__call__`` so that the object can be used like a common function. For instance: +Equivalently, the same operation is defined by the Python dunder method +``__call__`` so that the object can be used like a common function. + For instance: .. jupyter-execute:: print(f(9), f(25)) -Furthermore, the :meth:`rocketpy.Function.get_value` method can be used to get a list of outputs from a list of inputs: +Furthermore, both the :meth:`rocketpy.Function.get_value` and the dunder +``__call__`` method can be used to get a list of outputs from a list of inputs: .. jupyter-execute:: + # Using __call__ + print(f([1, 4, 9, 16, 25])) + + # Using get_value print(f.get_value([1, 4, 9, 16, 25])) b. Function Arithmetic ~~~~~~~~~~~~~~~~~~~~~~ -An important feature of the ``Function`` class is the ability to perform arithmetic operations between real values or even other ``Function`` objects. +An important feature of the ``Function`` class is the ability to perform +arithmetic operations between real values or even other ``Function`` objects. +The following operations are supported: + +- Addition: ``+``; +- Subtraction: ``-``; +- Multiplication: ``*``; +- Division: ``/``; +- Exponentiation: ``**``. .. jupyter-execute:: From 277a120f5b927631c6d7d04201c789743ff245c4 Mon Sep 17 00:00:00 2001 From: MateusStano Date: Sat, 18 Nov 2023 22:20:01 +0100 Subject: [PATCH 10/10] DOCS: link to usage page in Function reference --- docs/reference/classes/Function.rst | 2 ++ docs/user/function.rst | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/reference/classes/Function.rst b/docs/reference/classes/Function.rst index 2ad16d459..423123945 100644 --- a/docs/reference/classes/Function.rst +++ b/docs/reference/classes/Function.rst @@ -1,5 +1,7 @@ Function Classes ---------------- +.. seealso:: :doc:`Function Class Usage ` + .. autoclass:: rocketpy.Function :members: \ No newline at end of file diff --git a/docs/user/function.rst b/docs/user/function.rst index 8699946ee..bd40d4dad 100644 --- a/docs/user/function.rst +++ b/docs/user/function.rst @@ -279,6 +279,8 @@ The following operations are supported: - Division: ``/``; - Exponentiation: ``**``. +Let's see some examples of these operations: + .. jupyter-execute:: import numpy as np