Bonjour à tous,
Je me suis récemment mis au Python pour des besoins pros (ingénieur en thermohydraulique). Il s’avère en effet que j'ai un grand nombre de simulations à réaliser et j'ai pensé utiliser un script python pour automatiser tout cela. Pour fixer les idées, j'ai un modèle 3D et je dois résoudre quelques 2000 (!!!) simulations thermiques (FEA) sur ce modèle en faisant varier plusieurs paramètres impactant les conditions aux limites. A cet effet, j'utilise un code python, pyNukiyama, qui me fournit les conditions aux limites à imposer aux bords de ma simulation avec un triplet donné (Vitesse_eau, Pression_eau, Temp_eau). Tout marche, donc super ! Oui mais, le code est instable...
Je veux dire par là, lorsque je lance la simulation (pour 250 calculs à réaliser par exemple), elle plante au bout de quelques calculs (après l'avoir lancé et relancé, le nombre semble varier de manière aléatoire 10 à 80 calculs...). Je pense que personne ici ne sera spécialiste de Ansys Mechanical (enfin peut-être que si avec de la chance !). Cependant, codant sur Python depuis 2024 (et oui un mois déjà !) je me dis qu'il y a peut-être (sans doutes !) de grosses fautes de codage qui peuvent expliquer cette instabilité...

Voici le code :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import os
import sys
import logging
import csv
sys.path.append(os.getcwd()+"\\pyNukiyama") #Je spécifie le chemin de pyNukiyama afin que le programme le trouve
import pyNukiyama #j'importe pyNukiyama pour l'utiliser aprés
#ensuite je vais importer un certains nombre de modules me permettant d'appeler Ansys Mechanical dans le script
from ansys.mechanical.core import App
from ansys.mechanical.core.embedding.logger import (Configuration,Logger)
Configuration.configure(level=logging.WARNING,to_stdout=True)
app = App(version=232)
print(app)
Logger.error("message")
 
# Extract the global API entry points (available from built-in Mechanical scripting)
from ansys.mechanical.core import global_variables
# Merge them into your Python global variables
globals().update(global_variables(app))
 
#Ici je charge le fichier sur lequel je travail
filenameMechDb = r"D14_Cu.mechdat"
filepath = r"C:\Users\...\Documents\pyNukiNical"
filepathMechDb = os.path.join(filepath,filenameMechDb)
 
#Je peux enfin ouvrir le fichier de travail dans l'environnement Ansys Mechanical chargé
ExtAPI.DataModel.Project.Open(filepathMechDb)
 
#Je déclare les listes vides dont je vais avoir besoin
V=[]
iV=[]
P=[]
iP=[]
T=[]
iT=[]
Flux=[]
 
Tmin = 10 #Je défini ma température min pour pyNukiyama
Tmax = 350 #Je défini ma température max pour pyNukiyama
Geom = "s" #s pour tube lisse
Diam = 0.014 #Diamétre hydraulique en m
 
#Je charge mes données d'entrées enregistrées dans le document Inputs.csv
with open('Inputs.csv') as fichier_csv:
    reader = csv.DictReader(fichier_csv, delimiter=';')
    for ligne in reader:
        V.append(float(ligne['Vwater']))
        iV.append(float(ligne['iVwater']))
        P.append(float(ligne['Pwater']))
        iP.append(float(ligne['iPwater']))
        T.append(float(ligne['Twater']))
        iT.append(float(ligne['iTwater']))
        Flux.append(float(ligne['Flux']))
 
#Je prépare les listes pour les résultats
Resultats = []
Resultats.append(["Vitesse eau m/s", "Pression eau Bar", "Temp eau C", "Flux Thermique Impose W/m^2","TC1 C", "TC2 C", "TC3 C", "Flux chaleur max paroi mouillee en W/m²", "Temp surface mouillee max C", "Temp surface chauffee max en C", "Temp surface chauffee min en C"])
Resultats_final=[]
Resultats_final.append(["V [m/s]","P [bars]", "T [C]", "Flux [W/m^2]","Max TC1 [C]","Max TC2 [C]","Max TC3 [C]", "Min TC1 [C]","Min TC2 [C]","Min TC3 [C]"])
 
Compteur = 0
 
for Vwater, iVwater, Pwater, iPwater, Twater, iTwater, Flux_Thermique in zip(V, iV, P, iP, T, iT, Flux):
    TC1=[]
    TC2=[]
    TC3=[]
    for Veau in [Vwater-iVwater, Vwater+iVwater]:
        for Peau in [Pwater - iPwater, Pwater + iPwater]:
            for Teau in [Twater - iTwater, Twater + iTwater]:
 
                # je vais charger nukiyama avec les données spécifiées juste avant
                nuki = pyNukiyama.pyNukiyama(Geom, Diam, 0, 0, 0, 0, 0, 0, Diam, Teau, Peau, Veau, Tmin, Tmax)
                #On peut réaliser les calculs nécessaires avec nuki bien définie
                nuki.waterParameters()
                nuki.siederTate()
                nuki.berglesRohsenhow()
                nuki.CHF()
 
                convData = []
 
                convData.append([Veau,Peau,Teau]) #On stocke les grandeurs thermohydrauliques en en-tête des données de convection
 
                for i in range(len(nuki.Tw_cut)):
                    Wall_temperature = nuki.Tw_cut[i]
                    H_value = nuki.hTot_cut[i]
                    Unit = "W m^-1 m^-1 C^-1"
                    convData.append([Wall_temperature, H_value, Unit])
 
                #On spécifie le nombre de coeurs pour le calcul, attention à ne pas dépasser le nombre dispo sinon ça foire 
                config=ExtAPI.Application.SolveConfigurations["Mon poste de travail"]
                config.SolveProcessSettings.MaxNumberOfCores=12
 
                #D'abord on rentre la température initiale pour le solveur pseudo-transient, je la met égale à la température de l'eau en entrée    
                Anal = DataModel.Project.Model.Analyses[0]
                Anal.Children[0].InitialTemperatureValue = Quantity(float(convData[0][2]), "C")
 
                # On remplit ensuite la partie convection
                Anal.Children[2].AmbientTemperature.Output.SetDiscreteValue(0,Quantity(float(convData[0][2]), "C")) #Ici on rentre la température moyenne du fluide, idem à la valeur de la température initiale du solveur
 
                #On commence par initialiser la matrice de convection avec les bonnes dimensions pour la remplir ensuite...
                #Penser au -1 pour tenir compte du fait que l'en tête contient les données thermohydrauliques
                #Par ailleurs, j'ajoute 1000 à la valeur entrée pour que le remplissage se passe sans encombres dans la prochaine étape (Mechanical trie automatiquement par ordre T° croissante les valeurs de convection ce qui pose problème au moment du remplissage de manière croissante)
                Anal.Children[2].FilmCoefficient.Inputs[0].DiscreteValues = [Quantity(i+1000, "C") for i in range(len(convData)-1)] 
 
 
                #Et on remplit les valeurs la température et le coefficient de convection associé...
                for i in range(1,len(convData)):
                   Anal.Children[2].FilmCoefficient.Inputs[0].SetDiscreteValue(i-1, Quantity(float(convData[i][0]), "C"))
                   Anal.Children[2].FilmCoefficient.Output.SetDiscreteValue(i-1, Quantity(float(convData[i][1]), convData[i][2]))
 
                #On rentre la valeur de flux incident 
                Anal.Children[3].Magnitude.Output.SetDiscreteValue(0, Quantity(float(Flux_Thermique), "W m^-1 m^-1"))
 
                # On résout leproblème désormais bien posé
                Anal.Solve()
 
                Temp_Paroi_Mouillee_Max = DataModel.GetObjectsByName("Temp_Paroi_Mouillee")[0].Maximum
                Temp_Paroi_Mouillee_Min = DataModel.GetObjectsByName("Temp_Paroi_Mouillee")[0].Minimum
                Flux_Chaleur_Paroi_Max = DataModel.GetObjectsByName("Flux de chaleur total")[0].Maximum
                Flux_Chaleur_Paroi_Min = DataModel.GetObjectsByName("Flux de chaleur total")[0].Minimum
                Temp_Surface_Max = DataModel.GetObjectsByName("Temp Surface")[0].Maximum
                Temp_Surface_Min = DataModel.GetObjectsByName("Temp Surface")[0].Minimum
 
                if Flux_Chaleur_Paroi_Max.Value>=nuki.wchf or Temp_Paroi_Mouillee_Max.Value>=nuki.tchf:
                    break
 
                #On peut ensuite récupérer les données qui nous interessent !
                TC1.append(DataModel.GetObjectsByName("TC1")[0].MaximumTemperature.Value)
                TC2.append(DataModel.GetObjectsByName("TC2")[0].MaximumTemperature.Value)
                TC3.append(DataModel.GetObjectsByName("TC3")[0].MaximumTemperature.Value)
 
 
                Resultats.append([Veau,Peau,Teau,Flux_Thermique,DataModel.GetObjectsByName("TC1")[0].MaximumTemperature.Value,DataModel.GetObjectsByName("TC2")[0].MaximumTemperature.Value,DataModel.GetObjectsByName("TC3")[0].MaximumTemperature.Value,Flux_Chaleur_Paroi_Max.Value, Temp_Paroi_Mouillee_Max.Value, Temp_Surface_Max.Value, Temp_Surface_Min.Value])
 
                with open(f'Resultats_Complets.csv', 'w', newline='') as fichier_resultats:
                    writer = csv.writer(fichier_resultats, delimiter=',')
                    writer.writerows(Resultats) 
 
                Compteur = Compteur + 1
                print(Compteur)
 
    Resultats_final.append([Vwater, Pwater, Twater, Flux_Thermique, max(TC1), max(TC2), max(TC3), min(TC1), min(TC2), min(TC3)])
 
    with open(f'Resultats.csv', 'w', newline='') as fichier_resultats:
        writer = csv.writer(fichier_resultats, delimiter=',')
        writer.writerows(Resultats_final)
Code executé avec Spyder 5.5.0 et Python 3.8.10.
Globalement, lorsque ça plante, deux types de messages apparaissent (pas en même temps !):
- fatal python error: aborted (puis une référence au kernel de Python)
- windows fatal exception : access violation


Merci !
Pierre