Begin with a simple riveted assembly

# Begin with a simple riveted assembly

The objective is to build a simple bot to quickly introduce our mindset in bot development. The bot proposes to generate all connectable solutions based on rivet between two panels. The next figure shows this one system solved with 2, 3, and 4 rivets. But which is the best one? And how to generate all solutions? ## 1 - DessIA methodology

We suggest you cut the bot creation process into a functional phase and a development phase. To build a bot, you have to answer two questions:

• What is my pain point?
• How to build a bot? The first question can be answered by an engineer without any programming knowledge while the second phase must be done by a Python developer.

## 2 - Why a bot? What is my pain point?

We suggest you split this question into 4 sub-questions. The global mindset is to start from the big picture to the knowledge management. First of all, we need to detail our problem, and then to explain how we usually solve it, what are the metrics needed to do this work, and then how to organize the engineering system in order to estimate these metrics.

### 2.1 - What is the problem I want to automate/optimize?

With this simple rivet example, I want to generate all rivets and give their mass with an estimation of the price. At this point, I list all needed inputs: for the current usecase the input is a sheet with all data needed to describe the rivets.

### 2.2 - How do I do normally it?

If I had to perform this kind of analysis, I have 3 different tasks: generating all assemblies, then quoting each assembly to visualize all assembly to choose the best scenario. ### 2.3 - What metric do I need?

To achieve the previous step, I need to determine a price for each rivet. With my feedback I can define a price with the following equation ('price_factor' is one parameter I know with my feedback) by applying a coefficient to the volume ratio between the rivet's raw volume and the rivet's volume. This price defines an energy cost to distort the rivet from a cylinder to its final shape.

${\small{\mathsf{price}= \mathsf{number \text{\textunderscore} rivet} \cdot \mathsf{price \text{\textunderscore} rivet} + \frac {\small \pi} {\small 4} \cdot \mathsf{diameter \text{\textunderscore} hole^{2}} \cdot \mathsf{factor \text{\textunderscore} price \text{\textunderscore} machining}}}$.

In the same way, a reliability metric can be simulated based on 'factor_reliability' (depends on the feedback)

${\small{\mathsf{reliability}= \frac {\small 1} {\small \mathsf{length \text{\textunderscore} panel} \cdot \mathsf{number \text{\textunderscore} rivet}} \cdot \mathsf{factor \text{\textunderscore} reliability}}}$

### 2.4 - How to organize engineering to define these metrics? ## 3 - How to build my bot?

First we use the DessIA methodology for bot development:

• Define engineering rules
• Create elementary generator & optimizer We invite you to clone tutorials (opens in a new tab) and open tutorial1 to understand how it is built. The script1_tuto1 is available in the script folder. Before running script1, you should open a command prompt and change the directory to the tutorials folder and execute the following command:

python setup.py develop --user
• It is possible that python does not work. In this case, you should replace by: python3 or python3.7 or python3.8 If you have any trouble, please check 'Get Started' section and 'Installation'

### 3.1 - Describe your engineering system

First of all, we need to describe our problem by creating class, which are elements of our system.

class Panel(DessiaObject):
_standalone_in_db = True
def __init__(self, length: float, height: float,
thickness: float, mass: float = None,
name: str = ''):

class PanelCombination(DessiaObject):
_standalone_in_db = True
def __init__(self, panels: List[Panel], grids: List[vm.Point3D], mass:float=None,
name: str = ''):

class Rivet(DessiaObject):
_standalone_in_db = True
def __init__(self, rivet_diameter: float, rivet_length: float,
name: str = ''):

class PanelAssembly(DessiaObject):
_standalone_in_db = True
def __init__(self, panel_combination: PanelCombination,
rivet: Rivet, grids: List[vm.Point3D],
number_rivet1: int, number_rivet2: int,
number_rivet: int = None,
mass: float = None,
name: str = ''):   
• Panel needs at least three inputs: length, height and thickness. These parameters are explained in the schema above. You have an example in the script1 with the variable p1.
• PanelCombination needs at least two inputs: list of panels, list of vm.Point3D which are generated thanks to volmdlr. The second list refers to the position of each panel's center. This method calculates the intersection area between the panels. You have access to the area in the variable sol.
• Rivet needs at least four inputs explained in the schema above. For more information, please check 'Get Started' section and 'Build your first bot'.
• PanelAssembly needs multiple inputs, for example: PanelCombination, the Rivet used, a list of vm.Point3D which references the position of each Rivet. These inputs will be filled in thanks to solutions generated by the Generator.

### 3.2 - Define engineering rules

Before generating solutions, we need to mention our rules to solve the problem which is the assembly of panels. Indeed, we need to define the number of rivets necessary for the assembly.

class Rule(DessiaObject):
_standalone_in_db = True
def __init__(self, minimum_ratio: float,
maximum_ratio: float, name: str = ''):
self.minimum_ratio = minimum_ratio
self.maximum_ratio = maximum_ratio
DessiaObject.__init__(self, name=name)
• Rule needs two inputs: minimum ratio and maximum ratio. These ratios will allow a calculation of all possibilities concerning the number of rivet in 'define_number_rivet' method. These ratios use methods known by field engineers.
minimum_ratio = diameter/maximum_distance_between_rivet
maximum_ratio = diameter/minimum_distance_between_rivet

### 3.3 - Create elementary generator & optimizer

It is time to generate all solutions which match with our specification.

class Generator(DessiaObject):
_standalone_in_db = True
_dessia_methods = ['generate']
def __init__(self, panel_combination: PanelCombination,
rivet: Rivet, rule: Rule, name: str = ''):
self.rule = rule
self.panel_combination = panel_combination
self.rivet = rivet
DessiaObject.__init__(self, name=name)
• Generator needs three inputs: PanelCombination, the Rivet use and rules. The ouputs will be a list of PanelAssembly. Each PanelAssembly will be a solution to the problem. You will have access to a 2D view in order to see all solutions available. Here is an example of solution.

### 3.4 - Build your workflow

The workflow1_tuto1 file is available in the script folder. The following code defines elementary boxes. First of all, 'tutorial1_rivetedassembly' module is imported as well as the 'dessia_common.workflow' module to be able to create a workflow.

The first block is the 'block_generator' in which we instanciate a Generator block. Then with the 'block_generate' block we run the 'generate' method of the Generator block. And with the 'display_reductor' block we define the data-visualization interface. (Relevant parameters are defined in a list called "list_attribute". This list is then set as a parameter of the function that generates the "display_reductor" block).

import tutorials.tutorial1_rivetedassembly as tuto
import dessia_common.workflow as wf

block_generator = wf.InstantiateModel(tuto.Generator, name='Generator')
method_type = wf.MethodType(class_=tuto.Generator, name='generate')
block_generate = wf.ModelMethod(method_type=method_type, name='generate')

list_attribute1 = ['number_rivet1', 'number_rivet2', 'number_rivet', 'mass']
display_reductor = wf.MultiPlot(list_attribute1, 1, name='Display Reductor')

Next, 'block_workflow' is defined as the list of all the blocks needed, along with 'pipe_worflow' which creates every pipe. The last line defines the 'workflow' with 'block_generate.outputs' the output port of the workflow (the display block doesn't have any output port).

block_workflow = [block_generator, block_generate, display_reductor]

pipe_worflow = [wf.Pipe(block_generator.outputs, block_generate.inputs),
wf.Pipe(block_generate.outputs, display_reductor.inputs)]

workflow = wf.Workflow(block_workflow, pipe_worflow, block_generate.outputs)

To check if the number of ports is ok, you can run the following command on the Python console:

workflow.plot_jointjs()

and you see the following figure By reading the name of an input port, you can easily check the port number.

Then, the workflow is run with Python after defining an 'input_values' dictionary that uses the same port number convention as previously defined. (A dictionary has the following structure: {key1: value1, key2: value2, ...}). Only the green ports are mandatory since a default value, which is the value taken as input if none has been set, has not been defined. Let's take the example of the Generator class. The class constructor has the following inputs: "panel_combination: PanelCombination, rivet: Rivet, rule: Rule, name: str = ' ' ". The "name" parameter has a the default value " ' ' " which means that no name has been given when instantiating a generator block, it will automatically be set to " ' ' ". However, "panel_combination" doesn't have any default value which is why defining it is mandatory. The last line execute the workflow with the 'input_values' variable.

import volmdlr as vm

p1 = tuto.Panel(1, 1, 0.01)
p2 = tuto.Panel(1.1, 1, 0.01)
r1 = tuto.Rivet(0.01, 0.05, 0.012, 0.005)
pc1 = tuto.PanelCombination([p1, p2], [vm.Point3D(0, 0, 0), vm.Point3D(0.7, 0.2, 0.01)])
rule1 = tuto.Rule(0.1, 0.2)

input_values = {workflow.index(block_generator.inputs): pc1,
workflow.index(block_generator.inputs): r1,
workflow.index(block_generator.inputs): rule1,
}

workflow_run = workflow.run(input_values)

A workflow is what we call a 'bot' and 'workflow_run' is the result generated by the bot.

In the tutorial1_rivetedassembly.py code, we have defined two methods: _pressure_applied and _fatigue_resistance. Indeed, we have decided to express solutions thanks to fatigue resistance, which is a pertinent way to choose the best solution.
Now, we would like to apply a force about 100 Newton on the assembly. By knowing how many rivets are present in the assembly, we can determine the pressure applied on each rivet thanks to _pressure_applied method. Thanks to algorithm, we can determine the minimum_distance between each rivet in each solution. Now, we create 2 ratios concerning: pressure and distance between each rivet. These ratios allow us to know the characteristics imposed to rivets in assembly regarding characteristics used during experimentation. We apply a security coefficient of about 3. Then, we have the fatigue resistance versus the number of rivets determined by experimentation, the 2 ratios and security coefficient. 