Getting started with python-engineering¶
Getting started with python-engineering is not difficult. These basic example will show you how you can unlock the full potential of the tool.
But first, a few notes on installation
Installation¶
python-engineering is written for Python 3.x. Downloading Anaconda3 is recommended for users not familiar with Python development.
With a valid installation of Python 3, you can simply install python-engineering using pip.
$ pip install python-engineering
You can import python-engineering into any project:
import pyeng
If you want to run the suite of unit tests for python-engineering, you will need to install the package nose. With nose installed, simply run the unit tests as follows. Test failures will indicate whether there are still missing dependencies in your installation. Please check the FAQ for further info or raise an issue via GitHub.
$ python manage.py test
Basic function calls with python-engineering¶
As an example, we will call the function which returns the pressure drop and Darcy Weissback friction factor for given pipe properties. We can essentially create the Moody diagram using this function.
We can start by importing the function from its module:
from pyeng.hydraulics.pipe_flow.pressure_calcs import pressuredrop_relativeroughness_moody
For most functions, python-engineering has a three-level hierarchy. The engineering discipline is the top level (in this case hydraulics), next you have the function category (pipe_flow) and below that is the function module (pressure_calcs). This three-tier model allows a fairly detailed classificiation of engineering functions. Users can find the engineering discipline, function category and function module by consulting the documentation.
Calling functions¶
python-engineering functions are called like any other Python function. The documentation provide extensive guidance on each function argument, the allowed range or options and units.
python-engineering will always return dictionaries to allow returning multiple outputs from a single function in a structured manner. To return an output value, the dictionary key will need to be specified. This can either be done in the function call or afterwards as shown below.
from pyeng.hydraulics.pipe_flow.pressure_calcs import pressuredrop_relativeroughness_moody
friction_factor = pressuredrop_relativeroughness_moody(1.0e6,
1.0,
"Water mains,old",
10.0,
5.0,
1050.0)['friction_factor [-]']
or
from pyeng.hydraulics.pipe_flow.pressure_calcs import pressuredrop_relativeroughness_moody
output = pressuredrop_relativeroughness_moody(1.0e6,
1.0,
"Water mains,old",
10.0,
5.0,
1050.0)
friction_factor = output['friction_factor [-]']
It is often more instructive for people reviewing your work to use keyword arguments for all function parameters.
from pyeng.hydraulics.pipe_flow.pressure_calcs import pressuredrop_relativeroughness_moody
friction_factor = pressuredrop_relativeroughness_moody(reynolds_number=1.0e6,
pipe_diameter=1.0,
pipe_material="Water mains,old",
pipe_length=10.0,
average_velocity=5.0,
fluid_density=1050.0)['friction_factor [-]']
Several python-engineering functions have optional parameters. Defaults will be used when values are not specified. Users can override the values of the default parameters by specifying them as keyword arguments in the function call. In the example below we are overriding the value of acceleration of gravity from the default of 9.81, rounding it to 10.0 m/s2. The function documentation clearly specifies which function arguments are optional together with their default value.
from pyeng.hydraulics.pipe_flow.pressure_calcs import pressuredrop_relativeroughness_moody
friction_factor = pressuredrop_relativeroughness_moody(reynolds_number=1.0e6,
pipe_diameter=1.0,
pipe_material="Water mains,old",
pipe_length=10.0,
average_velocity=5.0,
fluid_density=1050.0,
gravity_coefficient=10.0)['friction_factor [-]']
Function validation¶
python-engineering has parameter validation built in to its core. Using numerical parameters outside applicable ranges or string parameters which are not in a predefined list, is prevented using the built-in validation logic.
If parameters are outside there applicable ranges, the calculation will not go ahead. By default, python-engineering will fail silently, meaning that in case of a validation error, the code will return NaN for numerical outputs and None for string outputs. If several function calls are made in a loop, the function calls for which the validation executes correctly, will still complete as expected. As an example of this, let’s call the function for Reynolds numbers ranging from 1e2 to 1e9 and plot the results. The function below will return values for all but the first and last Reynolds number, since these two are outside the validation ranges, this is clearly observed in the plot. Even though validation errors occur, the plot is still generated without problems but no values are calculated where the input parameters are out of bounds.
from pyeng.hydraulics.pipe_flow.pressure_calcs import pressuredrop_relativeroughness_moody
import numpy as np
import matplotlib.pyplot as plt
reynolds_numbers = np.logspace(2,9,8) # [1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9]
friction_factors = []
for re in reynolds_numbers:
friction_factor = pressuredrop_relativeroughness_moody(reynolds_number=re,
pipe_diameter=1.0,
pipe_material="Water mains,old",
pipe_length=10.0,
average_velocity=5.0,
fluid_density=1050.0)['friction_factor [-]']
friction_factors.append(friction_factor)
plt.plot(reynolds_numbers, friction_factors)
plt.xscale('log')
plt.show()
When numerical results are needed for parameters outside validation ranges, the user has two choices:
- Switch of validation completely (not recommended)
- Expand the validation ranges by specifying additional keyword arguments
For the first option, the function call would look like this:
from pyeng.hydraulics.pipe_flow.pressure_calcs import pressuredrop_relativeroughness_moody
pressuredrop_relativeroughness_moody(reynolds_number=1e2,
pipe_diameter=1.0,
pipe_material="Water mains,old",
pipe_length=10.0,
average_velocity=5.0,
fluid_density=1050.0,
validate=False)['friction_factor [-]']
In this case, no validation whatsoever is carried out by adding validate as a keyword argument and setting it to False.
For the second (recommended) option, the code from the validation example would need to look as follows:
from pyeng.hydraulics.pipe_flow.pressure_calcs import pressuredrop_relativeroughness_moody
import numpy as np
import matplotlib.pyplot as plt
reynolds_numbers = np.logspace(2,9,8) # [1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9]
friction_factors = []
for re in reynolds_numbers:
friction_factor = pressuredrop_relativeroughness_moody(reynolds_number=re,
pipe_diameter=1.0,
pipe_material="Water mains,old",
pipe_length=10.0,
average_velocity=5.0,
fluid_density=1050.0,
reynolds_number__min=1e2,
reynolds_number__max=1e9)['friction_factor [-]']
friction_factors.append(friction_factor)
plt.plot(reynolds_numbers, friction_factors)
plt.xscale('log')
plt.show()
The code above will generate a plot containing all reynolds numbers, from 1e2 to 1e9. Adding keyword arguments
__min and/or __max to a function argument overrides the validation ranges. Note that overriding of validation ranges
is only possible for the given function call. Calling the function again without the override would again result in
the default behaviour.
The behaviour of the function in case of overriding of validation ranges depends on how it is coded. The function above works with interpolation and follows the logic of numpy’s interp function.
Error handling¶
Since python-engineering fails silently by default, the user might not know what is actualy going wrong when a value is not returned. After all, not all errors will be validation errors, errors could also be raised during the execution of the function itself.
If you want to see what is actually going when a value is not returned and get more details on where the error
is happening, you can override the default error handling behaviour. You can do this by specifying fail_silently=False
as an additional keywords argument. This will cause the function to fail with a full traceback of the error.
from pyeng.hydraulics.pipe_flow.pressure_calcs import pressuredrop_relativeroughness_moody
pressuredrop_relativeroughness_moody(reynolds_number=1e2,
pipe_diameter=1.0,
pipe_material="Water mains,old",
pipe_length=10.0,
average_velocity=5.0,
fluid_density=1050.0,
fail_silently=False)['friction_factor [-]']
The code above will raise a ValueError indicating the reynolds_number=1e2 is outside allowable bounds. It will also
say what the allowable minimum is. The function documentation will also provide this information for each input
parameter.
python-engineering class methods¶
For certain calculations, it is more meaningful to uses classes than functions. Sometimes, calculation entities need to be persistent throughout the execution of a program. A typical example of this are geometrical shapes. Geometrical shapes have multiple atributes which need to be calculated such as area, centroid location, … Creating the geometric shapes as objects allows the necessary flexibility.
Object creation¶
When objects are created, the necessary properties need to be specified. As an example, we can create a circle with a radius of 1m. When the circle object is created, the derived properties such as area will immediately be calculated. We can go ahead and print the circle area.
from pyeng.general.geometry.geom_2d import Circle
my_circle = Circle(radius=1.0)
print("Circle area = %.2fm2" % my_circle.centroid['area [m2]'])
Modification of object attributes¶
Now that the object my_circle of the Circle class exists, we can change its attributes. This will trigger recalculation. If we change the radius of the circle to 2m, we should see the effect on the area.
from pyeng.general.geometry.geom_2d import Circle
my_circle = Circle(radius=1.0)
print("Circle area = %.2fm2" % my_circle.centroid['area [m2]'])
my_circle.radius = 2.0
print("Updated circle area = %.2fm2" % my_circle.centroid['area [m2]'])
Validation and error handling¶
The documentation highlights which validation ranges or options are applicable for class attributes. When values are
outside allowable ranges, the calculation will fail silently, unless fail_silently=False is specified as an
additional keyword argument. This is illustrated in the example below:
# Silent failure, returning np.NaN
from pyeng.general.geometry.geom_2d import Circle
my_circle = Circle(radius=-1.0)
print("Circle area = %.2fm2" % my_circle.centroid['area [m2]'])
# Failure with error traceback
from pyeng.general.geometry.geom_2d import Circle
my_circle = Circle(radius=-1.0,fail_silently=False)