Un des inconvénients de la programmation mathématique de grandeurs physiques est la difficulté de suivre les grandeurs utilisées au fil des variables et des calculs et donc d’assurer la cohérence du système d’unité et des multiples et sous-multiples utilisés. Habituellement, ceci passe par un lourd système de commentaires, peu efficace pour détecter les erreurs. Il apparaît donc pertinent de déclarer explicitement les unités des variables utilisées afin d’en assurer la traçabilité.

Cette pratique offre trois autres avantages :

  1. la possibilité de convertir à la volée les grandeurs d’un système d’unité à l’autre (vital en Amérique du Nord où le système métrique cohabite avec le système impérial et provoque un crash de sonde spatial par décennie à cause de soucis d’unités)
  2. la possibilité de débugguer un algorithme par les dimensions des résultats et les erreurs renvoyées en cas de tentatives d’opération illicites sur des grandeurs différentes
  3. la possibilité de programmer avec des grandeur sans se soucier de les convertir à chaque étape dans le bon multiple.

Il existe différents modules Pythons pour venir à bien de cette tâche, présentés dans la vidéo ci-dessus, chacun ayant leurs forces et leurs faiblesses, et une intégration plus ou moins fine avec Numpy, qui est la base du calcul numérique sous Python. J’ai choisi quantities  pour ce faire, et je vais vous présenter un cas réel commenté : la conception d’un engrenage (dimension et résistance des matériaux)

Exemple et code source

L’exemple ci-dessous est typique de l’ingénierie nord-américaine : on doit dimensionner un train d’engrenages en tenant compte :

  • de la puissance mécanique à transmettre,
  • de la résistance mécanique de l’acier utilisé,
  • de la vitesse de rotation en sortie.

On doit donc déterminer :

Mais si les constantes physiques et les propriétés des matériaux sont en unités SI, les données du problème sont en unités impériales (dimensions en pouces) et en unités SI (puissance en watts). On a donc toutes les chances de se prendre les pieds dans le tapis en déroulant le calcul, et la NASA perd une sonde spatiale par décennie  à cause d’erreurs de conversions d’unités de ce type.

On veut donc assurér la cohérence des résultats au cours du calcul en gardant la traçabilité des grandeurs et en évitant les erreurs de conversion.

  1from pylab import *
  2import quantities as u
  3from tabulate import tabulate
  4
  5def inv(i):
  6  return tan(i) - i
  7
  8### Données
  9HB      = 250.              # dureté Rockwell B de l'acier
 10p_d     = 8 * 1/u.inch      # pas diamétral de l'engrenage
 11Delta_C = 0.1 * u.inch      # tolérance max de l'entraxe
 12R       = 0.999             # fiabilité
 13t       = 1E5               # nombre de cycles de service (durée de vie utile)
 14
 15C       = 5.5 * u.inch
 16n_1     = 1200.0 * u.rpm     # taux de rotation pignon (moteur)
 17n_2     = 475.0 * u.rpm      # taux de rotation roue (charge)
 18P_u     = 7.5 * u.kilowatt   # puissance mécanique transmise par l'engrenage
 19Q_v     = 9.                 # facteur de fabrication
 20S_p     = 80 * u.megapascal  # Contrainte plastique admissible de l'acier (non réversible)
 21FS      = 2.                 # Facteur de sécurité
 22
 23phi     = (20. * u.deg).rescale('rad') # angle de contact
 24
 25### Calculs des rayons primitifs approchés
 26i = n_1 / n_2
 27r_1 = C / (i + 1)
 28r_2 = i * r_1
 29d_1 = 2. * r_1
 30d_2 = 2. * r_2
 31
 32print """Calculs des rayons primitifs approchés
 33----------------------
 34  i     = %s   | Rapport de réduction
 35  r_1   = %s   | Rayon pignon
 36  r_2   = %s   | Rayon roue
 37""" % (i, r_1, r_2)
 38
 39### Calcul du module
 40W_t = 60 * P_u / (pi * d_1.rescale('m') * n_1)
 41m = 2.34 * sqrt(W_t / (Q_v * S_p))
 42
 43print """Module
 44----------------------
 45  W_t  = %s  | chargement tangentiel
 46  m    = %s  | module
 47""" % (W_t.rescale('N'), m.rescale('mm') )
 48
 49### Calcul du nombre de dents
 50Z_1 = p_d * d_1
 51Z_2 = p_d * d_2
 52
 53print """Nombre approximatif de dents
 54----------------------
 55  Z_1 = %s  | dents pignon
 56  Z_2 = %s  | dents roue
 57""" % (Z_1, Z_2)
 58
 59Z_1_m = array([[floor(Z_1), ceil(Z_1)]])
 60Z_2_m = array([[floor(Z_2)], [ceil(Z_2)]])
 61i_m = (1. / Z_1_m) * Z_2_m
 62C_d = 0.5 * Z_1_m / p_d + 0.5 * Z_2_m / p_d
 63
 64Z_1 = Z_1_m[1]
 65Z_2 = Z_2_m[0,0]
 66C = C_d[0,1]
 67i = i_m[0,1]
 68
 69print """Sélection de la combinaison pignon/roue
 70----------------------
 71Rapports de réduction
 72%s
 73Entraxes
 74%s
 75Combinaison choisie
 76  Z_1  = %i dents
 77  Z_2  = %i dents
 78  C    = %s  | entraxe
 79  i    = %s    | rapport de transmission
 80""" % (tabulate(column_stack((Z_2_m,i_m)), headers=Z_1_m.astype(int), tablefmt="fancy_grid"),
 81              tabulate(column_stack((Z_2_m,C_d)), headers=Z_1_m.astype(int), tablefmt="fancy_grid"),
 82              Z_1, Z_2, C, i
 83  )
 84
 85### Dimension des dents
 86a = 1. / p_d
 87b = 1.25 / p_d
 88h = a + b
 89p_c = pi / p_d
 90t = pi / (2. * p_d)
 91r_f = 0.35 / p_d
 92
 93print """Dimension des dents
 94----------------------
 95  a    = %s  | Saillie
 96  b    = %s  | Creux
 97  h    = %s  | Hauteur
 98  p_c  = %s  | Pas primitif
 99  t    = %s  | Épaisseur au cercle primitif
100  r_f  = %s  | Rayon à la racine
101""" % (a, b , h, p_c, t, r_f)
102
103class engrenage:
104  def __init__(self, Z, p_d, a, b, phi):
105    self.d = Z / p_d
106    self.r = self.d / 2
107    self.d_a = self.d + 2*a
108    self.a = (self.d_a - self.d)/2
109    self.d_c = self.d - 2*b
110    self.r_b = self.r * cos(phi)
111    self.p_b = 2*pi*self.r_b /Z
112
113### Dimensions du pignon
114pignon = engrenage(Z_1, p_d, a, b, phi)
115
116print """Dimensions du pignon
117----------------------
118  d_1    = %s  | Diamètre primitif
119  d_a_1  = %s  | Diamètre saillie
120  d_c_1  = %s  | Diamètre creux
121  r_b_1  = %s  | Rayon de base
122  p_b    = %s  | Pas de base
123""" % (pignon.d, pignon.d_a, pignon.d_c, pignon.r_b, pignon.p_b)
124
125# Dimensions de la roue
126roue = engrenage(Z_2, p_d, a, b, phi)
127
128print """Dimensions de la roue
129----------------------
130  d_2     = %s
131  d_a_2   = %s
132  d_c_2   = %s
133  r_b_2   = %s
134  p_b     = %s
135""" % (roue.d, roue.d_a, roue.d_c, roue.r_b, roue.p_b)
136
137### Vérification du contact et interférence
138m = ( sqrt( (r_1 + a_1)**2 - (r_1 * cos(phi)) **2) + sqrt( (r_2 + a_2)**2 - (r_2 * cos(phi))**2) - C * sin(phi)) / p_b
139g = (Z_1**2 + 2 * Z_1 * Z_2) * sin(phi)**2 - 4*Z_2 - 4
140print """Contact et interférences
141----------------------
142  m            = %s
143  g(Z_1, Z_2)  = %s
144""" % (m, g)
145
146### Paramètres d'opération
147C_dot = C + Delta_C
148r_dot_1 = C_dot / (1 + i)
149r_dot_2 = i * r_dot_1
150phi_dot = arccos(C * cos(phi) / C_dot)
151t_dot_1 = r_dot_1 * t / r_1 - 2 * r_dot_1 * (inv(phi_dot) - inv(phi))
152t_dot_2 = r_dot_2 * t / r_2 - 2 * r_dot_2 * (inv(phi_dot) - inv(phi))
153B = 2 * C_dot * (inv(phi_dot) - inv(phi))
154
155print """Paramètres d'opération
156----------------------
157  r_dot_1   = %s
158  r_dot_2   = %s
159  phi_dot   = %s
160  t_dot_1   = %s
161  t_dot_2   = %s
162  B     = %s
163""" % (r_dot_1,
164          r_dot_2,
165          phi_dot.rescale('deg'),
166          t_dot_1,
167          t_dot_2,
168          B)
169
170### Résistance en flexion
171S_fb_dot = (6235 + 174 * HB - 0.126*HB**2)*u.lb / u.inch**2
172K_L = 1.5
173K_T = 1
174K_R = 1.25
175S_fb = S_fb_dot * K_L / (K_T * K_R)
176sigma_b = S_fb / FS
177
178# Équation de Lewis
179W_T = 33000 * P_u.rescale('hp') / (pi * d_1.rescale('feet') * n_1) # lb
180
181J = 0.37
182K_v = 0.837
183K_m = 1.6
184F = W_T * K_m * p_d / (K_v * sigma_b * J)
185
186print """Résistance en flexion
187----------------------
188  S_fb    = %s
189  sigma_b = %s
190  W_T     = %s [lb]
191  F       = %s [in]
192""" % (S_fb, sigma_b, W_T, F)
193
194### Largeur de dents
195C_L = 1.138
196C_H = 1
197C_T = 1
198C_R = 1.25
199S_dot_fc = (27000 + 364 * HB) * u.lb / u.inch**2
200
201S_fc = C_L * C_H * S_dot_fc / (C_T * C_R)
202
203C_p = 2300
204C_s = 1
205C_a = 1
206C_m = K_m
207C_v = K_v
208C_f = 1
209I = i * cos(phi) * sin(phi) / (2 * (i+1))
210sigma_c = S_fc / sqrt(FS)
211
212F_dot = C_p**2 * W_T/ (sigma_c**2 * I * d_1) * C_a * C_m * C_s * C_f / C_v
213
214print """Largeur des dents
215----------------------
216  S_dot_fc  = %s
217  S_fc      = %s
218  sigma_c   = %s
219  F_dot     = %s [in]
220""" % (S_dot_fc, S_fc, sigma_c, F_dot)</pre>

Résultat

 1Calculs des rayons primitifs approchés
 2----------------------
 3        i       = 2.52631578947 dimensionless   | Rapport de réduction
 4        r_1     = 1.55970149254 in      | Rayon pignon
 5        r_2     = 3.94029850746 in      | Rayon roue
 6
 7Module
 8----------------------
 9        W_t     = 14386.2491373 N       | chargement tangentiel
10        m       = 10.4598004135 mm      | module
11
12Nombre approximatif de dents
13----------------------
14        Z_1 = 24.9552238806 dimensionless       | dents pignon
15        Z_2 = 63.0447761194 dimensionless       | dents roue
16
17Sélection de la combinaison pignon/roue
18----------------------
19Rapports de réduction
20╒════╤═════════╤══════╕
21│    │    24   │   25 │
22╞════╪═════════╪══════╡
23│ 63 │ 2.625   │ 2.52 │
24├────┼─────────┼──────┤
25│ 64 │ 2.66667 │ 2.56 │
26╘════╧═════════╧══════╛
27Entraxes
28╒════╤════════╤════════╕
29│    │     24 │     25 │
30╞════╪════════╪════════╡
31│ 63 │ 5.4375 │ 5.5    │
32├────┼────────┼────────┤
33│ 64 │ 5.5    │ 5.5625 │
34╘════╧════════╧════════╛
35Combinaison choisie
36        Z_1     = 25 dents
37        Z_2     = 63 dents
38        C       = 5.5 in        | entraxe
39        i       = 2.52          | rapport de transmission
40
41Dimension des dents
42----------------------
43        a       = 0.125 in      | Saillie
44        b       = 0.15625 in    | Creux
45        h       = 0.28125 in    | Hauteur
46        p_c     = 0.392699081699 in     | Pas primitif
47        t       = 0.196349540849 in     | Épaisseur au cercle primitif
48        r_f     = 0.04375 in    | Rayon à la racine
49
50Dimensions du pignon
51----------------------
52        d_1     = 3.125 in      | Diamètre primitif
53        d_a_1   = 3.375 in      | Diamètre saillie
54        d_c_1   = 2.8125 in     | Diamètre creux
55        r_b_1   = 1.46826971998 in      | Rayon de base
56        p_b     = 0.369016429262 in     | Pas de base
57
58Dimensions de la roue
59----------------------
60        d_2     = 7.875 in
61        d_a_2   = 8.125 in
62        d_c_2   = 7.5625 in
63        r_b_2   = 3.70003969434 in
64        p_b     = 0.369016429262 in
65
66Contact et interférences
67----------------------
68        m               = 1.70177405756 dimensionless
69        g(Z_1, Z_2)     = 185.591113613 dimensionless
70
71Paramètres d'opération
72----------------------
73        r_dot_1         = 1.59090909091 in
74        r_dot_2         = 4.00909090909 in
75        phi_dot         = 22.644361906 deg
76        t_dot_1         = 0.177860802115 in
77        t_dot_2         = 0.143285601241 in
78        B               = 0.0789093806371 in
79
80Résistance en flexion
81----------------------
82        S_fb    = 50232.0 lb/in**2
83        sigma_b = 25116.0 lb/in**2
84        W_T     = 338.68017762 hp/(ft*rpm) [lb]
85        F       = 0.557342411117 in*hp/(lb*ft*rpm) [in]
86
87Largeur des dents
88----------------------
89        S_dot_fc        = 118000.0 lb/in**2
90        S_fc            = 107427.2 lb/in**2
91        sigma_c         = 75962.5016039 lb/in**2
92        F_dot           = 1.65388227338 in**3*hp/(lb**2*ft*rpm) [in]

Commentaires

Sur la procédure de calcul, on remarque aux sections Dimension du pignon et Dimension de la roue que les calculs étant les mêmes, le code a été factorisé en passant par une classe. C’est l’une des beautés de la programmation scientifique orientée objet qui est grandement facilitée avec Python, et plus complexe – quoique possible – sous Matlab par exemple.

Sur les unités, on peut vérifier la cohérences des résultats, y compris sur les rapports de transmission sans dimension.

Cependant, on remaque un problème de calcul sur la variable W_T (section Résistance en flexion) dont la dimension est hp/(ft*rpm) en raison du facteur de correction 33000 sans unité précisée. Ainsi W_T est homogène à des livres force mais le passage dans cette unité via le module quantities introduit des constantes de conversion déjà prises en compte dans le 33 000. Le problème se propage à F.

Note : La valeur 33000 a été reprise telle quelle d’un corrigé qui ne mentionnait pas sa dimension ni son origine (Car les formules de dimensionnement d’éléments de machines relèvent parfois de la sorcellerie).

À cette exception près (qui mériterait seulement un retour à la documentation), on remarque que le passage d’un système d’unités à l’autre est presque indolore.

Source