TrioCFD 1.9.9_beta
TrioCFD documentation
Loading...
Searching...
No Matches
Probleme_Couple.cpp
1/****************************************************************************
2* Copyright (c) 2026, CEA
3* All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9*
10* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
11* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
12* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13*
14*****************************************************************************/
15
16#include <Schema_Euler_Implicite.h>
17#include <Probleme_Couple.h>
18#include <EcrFicCollecte.h>
19#include <Postraitement.h>
20#include <Equation_base.h>
21#include <Milieu_base.h>
22#include <TRUSTTabs.h>
23#include <Domaine_VF.h>
24#include <TRUST_2_PDI.h>
25#include <Ecrire_YAML.h>
26
27Implemente_instanciable(Probleme_Couple,"Probleme_Couple",Couplage_U);
28Implemente_instanciable(Probleme_Couple_Point_Fixe,"Probleme_Couple_Point_Fixe",Probleme_Couple);
29// XD coupled_problem pb_gen_base probleme_couple INHERITS_BRACE This instruction causes a probleme_couple type object
30// XD_CONT to be created. This type of object has an associated problem list, that is, the coupling of n problems among
31// XD_CONT them may be processed. Coupling between these problems is carried out explicitly via conditions at particular
32// XD_CONT contact limits. Each problem may be associated either with the Associate keyword or with the Read/groupes
33// XD_CONT keywords. The difference is that in the first case, the four problems exchange values then calculate their
34// XD_CONT timestep, rather in the second case, the same strategy is used for all the problems listed inside one group,
35// XD_CONT but the second group of problem exchange values with the first group of problems after the first group did
36// XD_CONT its timestep. So, the first case may then also be written like this: NL2 Probleme_Couple pbc NL2 Read pbc {
37// XD_CONT groupes { { pb1 , pb2 , pb3 , pb4 } } } NL2 There is a physical environment per problem (however, the same
38// XD_CONT physical environment could be common to several problems). NL2 Each problem is resolved in a domain. NL2
39// XD_CONT Warning : Presently, coupling requires coincident meshes. In case of non-coincident meshes, boundary
40// XD_CONT condition \'paroi_contact\' in VEF returns error message (see paroi_contact for correcting procedure).
41// XD attr groupes list_list_nom groupes OPT { groupes { { pb1 , pb2 } , { pb3 , pb4 } } }
42// XD ref domaine_2 domaine
43// XD ref pb_1 Pb_base
44// XD ref pb_2 Pb_base
45// XD ref pb_3 Pb_base
46// XD ref pb_4 Pb_base
47// XD ref scheme_2 schema_temps_base
48
49///////////////////////////////////////////////
50// //
51// Implementation of the Problem interface //
52// //
53///////////////////////////////////////////////
54
56{
57 /*
58 double& residu_max = schema_temps().residu();
59 double test=residu_max;
60 for (int i=1; i<nb_problemes(); i++)
61 residu_max = std::max(residu_max,sch_clones[i]->residu());
62 if (test!=residu_max) abort();
63 */
64 bool ok = Couplage_U::initTimeStep(dt);
65 return ok;
66}
67
68double Probleme_Couple::computeTimeStep(bool& stop) const
69{
70 double dt_=DMAXFLOAT;
71
72 // We take the minimum dt.
73 // We stop if one of the problems wants to stop.
74 for (int i=0; i<nb_problemes(); i++)
75 {
76 double dt1=probleme(i).computeTimeStep(stop);
77 if (stop)
78 return 0;
79 if (dt1<dt_)
80 dt_=dt1;
81 }
82 return dt_;
83}
84
86{
87 // Trigger domain-specific time step logic at most once per distinct domain,
88 // even if several problems share it.
89 std::set<const Domaine*> processed_domains;
90 for (int i = 0; i < nb_problemes(); i++)
91 {
92 Probleme_base& pb = ref_cast(Probleme_base, probleme(i));
93 Domaine& dom = pb.domaine();
94 if (processed_domains.insert(&dom).second)
95 dom.mettre_a_jour(schema_temps().temps_courant(), pb.domaine_dis(), pb);
96 }
97
98 // WEC: To be changed!!!!
99 if (sch_clones.size())
100 if (sub_type(Schema_Euler_Implicite,schema_temps()))
101 {
103 int ok = 1, cv;
104 cv = sch_eul_imp.faire_un_pas_de_temps_pb_couple(*this, ok);
105 if (!ok) return false;
106 // we propagate a certain number of things to the clones
107 for (int i=1; i<sch_clones.size(); i++)
108 {
109 sch_clones[i]->facteur_securite_pas()=schema_temps().facteur_securite_pas();
110 sch_clones[i]->set_stationnaire_atteint()=schema_temps().stationnaire_atteint();
111 sch_clones[i]->residu()=schema_temps().residu();
112 }
113 return cv;
114 }
115
117 double& residu_max = schema_temps().residu();
118 for (int i=1; i<nb_problemes(); i++)
119 residu_max = std::max(residu_max,ref_cast(Probleme_base,probleme(i)).schema_temps().residu());
120 return ok;
121}
122
124{
125 std::vector<Schema_Euler_Implicite*> per_pb_schemas;
126 per_pb_schemas.reserve(nb_problemes());
127 for (int i = 0; i < nb_problemes(); i++)
128 {
129 Schema_Temps_base& sch_i = ref_cast(Probleme_base, probleme(i)).schema_temps();
130 if (!sub_type(Schema_Euler_Implicite, sch_i))
131 {
132 per_pb_schemas.clear();
133 break;
134 }
135 per_pb_schemas.push_back(&ref_cast(Schema_Euler_Implicite, sch_i));
136 }
137
138 if (int(per_pb_schemas.size()) != nb_problemes()) Process::exit("Probleme_Couple_Point_Fixe::solveTimeStep only works with problems using Schema_Euler_Implicite");
139
140 for (int i = 0; i < nb_problemes(); i++)
141 per_pb_schemas[i]->Initialiser_Champs(ref_cast(Probleme_base, probleme(i)));
142
143 const int max_fp_iter = 1000;
144 bool converged = false;
145 int compteur = 0;
146 int ok = 1;
147 int nb_iter_min = 0; // minimum number of iterations before testing convergence
148
149 while (compteur < nb_iter_min || (!converged && ok && compteur < max_fp_iter))
150 {
151 compteur++;
152 converged = true;
153
154 // 2. maillage
155 std::set<const Domaine*> processed_domains;
156 for (int i = 0; i < nb_problemes(); i++)
157 {
158 Probleme_base& pb = ref_cast(Probleme_base, probleme(i));
159 Domaine& dom = pb.domaine();
160 if (processed_domains.insert(&dom).second)
161 dom.mettre_a_jour(schema_temps().temps_courant(), pb.domaine_dis(), pb);
162 }
163
164 for (int i = 0; i < nb_problemes(); i++)
165 {
166 Probleme_base& pb = ref_cast(Probleme_base, probleme(i));
167 const double temps = per_pb_schemas[i]->temps_courant() + per_pb_schemas[i]->pas_de_temps();
168 for (int eq = 0; eq < pb.nombre_d_equations(); eq++)
170 }
171
172 // 1. problems
173 for (int i = 0; ok && i < nb_problemes(); i++)
174 {
175 Probleme_base& pb = ref_cast(Probleme_base, probleme(i));
177 const int cv_pb = per_pb_schemas[i]->Iterer_Pb(pb, compteur, ok);
178 converged = converged && cv_pb;
179 }
180 }
181
182 if (!ok || !converged)
183 {
184 for (int i = 0; i < nb_problemes(); i++)
185 per_pb_schemas[i]->notify_failed_timestep();
186 if (limpr())
187 Cerr << le_nom() << " : Failure in Probleme_Couple_Point_Fixe::solveTimeStep after " << compteur << " iterations." << finl;
188 return false;
189 }
190
191 Cout << "Fixed-point convergence at t = " << schema_temps().temps_courant() << " in " << compteur << " iterations." << finl;
192 for (int i = 0; i < nb_problemes(); i++)
193 per_pb_schemas[i]->test_stationnaire(ref_cast(Probleme_base, probleme(i)));
194
195 double& residu_max = schema_temps().residu();
196 for (int i = 1; i < nb_problemes(); i++)
197 residu_max = std::max(residu_max, ref_cast(Probleme_base, probleme(i)).schema_temps().residu());
198
199 return ok;
200}
201
203{
204 bool ok=true;
205 converged=true;
206
207 int debut_gr=0;
208 int fin_gr=0;
209 int gr=0;
210 while(ok && fin_gr<nb_problemes())
211 {
212
213 if (gr<groupes.size_array())
214 fin_gr += groupes[gr++];
215 else
216 fin_gr=nb_problemes();
217
218 for(int i=debut_gr; ok && i<fin_gr; i++)
219 ok &= probleme(i).updateGivenFields();
220
221 for(int i=debut_gr; ok && i<fin_gr; i++)
222 {
223 bool cv;
224 ok &= probleme(i).iterateTimeStep(cv);
225 converged = converged && cv;
226 }
227
228 debut_gr=fin_gr;
229 }
230
231 return ok;
232}
233
234////////////////////////////////////////////////////////
235// //
236// End of the implementation of the Problem interface //
237// //
238////////////////////////////////////////////////////////
239
241{
242
243 Cerr << "Reading of Probleme_Couple " << le_nom() << finl;
244
245 Motcle motlu;
246 is >> motlu;
247 if (motlu != Motcle("{"))
248 {
249 Cerr << "We expected { to start to read the Probleme_Couple" << finl;
250 exit();
251 }
252
253 is >> motlu;
254 while (motlu!=Motcle("}")) // end of readOn
255 {
256
257 if (motlu != Motcle("groupes"))
258 {
259 Cerr << "The keyword " << motlu << " is not understood" << finl;
260 exit();
261 }
262
263 if (nb_problemes())
264 {
265 Cerr << "We can associate problems to Probleme_Couple" << finl;
266 Cerr << "* either by \"associer prob_couple pb\" (in which case they are all in the same group)" << finl;
267 Cerr << "* either by the keyword \"groupes\" while reading the object Probleme_Couple" << finl;
268 Cerr << "but not both!" << finl;
269 }
270 assert(nb_problemes()==0);
271
272 LIST(LIST(Nom)) les_noms;
273 is >> les_noms;
274
275 groupes.resize_array(les_noms.size());
276 for (int i=0; i<les_noms.size(); i++)
277 {
278 groupes[i]=les_noms[i].size();
279 for (int j=0; j<les_noms[i].size(); j++)
280 {
281 Nom nom_pb=les_noms[i][j];
282 Objet_U& ob=Interprete::objet(nom_pb);
283 Probleme_base& pb=ref_cast(Probleme_base,ob);
284 ajouter(pb);
285 }
286 }
287
288 is >> motlu;
289 }
290
291 return is;
292}
293
294/*! @brief Overrides Objet_U::printOn(Sortie&): prints the coupled problems to an output stream.
295 *
296 * @param (Sortie& os) the output stream to print to
297 * @return (Sortie&) the modified output stream
298 */
300{
301 for(int i=1; i< nb_problemes(); i++)
302 os << probleme(i) << finl;
303
304 return os;
305}
306
309
311{
312 // No fields coming from outside the coupling.
313 // Internal exchanges are done during iterateTimeStep.
314 return true;
315}
316
317/*! @brief Adds a problem to the list of coupled problems.
318 *
319 * Sets up the reference from the problem back to this.
320 * Verifies that the conduction, thHyd order is respected.
321 *
322 * @param (Probleme_base& pb) the problem to add to the coupling
323 */
325{
326 addProblem(pb);
327 pb.associer_pb_couple(*this);
328
329 int nb_pb=nb_problemes();
330
331
332 Nom nom_pb=probleme(nb_pb-1).que_suis_je();
333 if (nb_pb!=1
334 && probleme(0).que_suis_je()=="Pb_Conduction"
335 && nom_pb.prefix("Turbulent") != probleme(nb_pb-1).que_suis_je())
336 {
337 Cerr << finl;
338 Cerr << "Warning !" << finl;
339 Cerr << "You define a conduction problem named "<<probleme(0).le_nom()<<" before your turbulent thermalhydraulic problem named "<<probleme(nb_pb-1).le_nom()<<"." << finl;
340 Cerr << "Please, define first your turbulent thermalhydraulic problem in your data file like:" << finl;
341 Cerr << finl;
342 Cerr << "Probleme_Couple "<<le_nom()<< finl;
343 for (int i=nb_pb-1; i>=0; i--)
344 Cerr << "Associer "<<le_nom()<<" "<<probleme(i).le_nom()<<finl;
345 Cerr << finl;
346 exit();
347 }
348}
350{
351 // Assign value 1 to schema_impr_ for the time scheme of problem 0 and 0 for the others. Only one scheme should print.
352 for (int i = 0; i < nb_problemes(); i++)
353 {
354 Probleme_base& pb = ref_cast(Probleme_base, probleme(i));
355 pb.schema_temps().schema_impr() = (i == 0);
356 }
357
359}
360
361/*! @brief Overrides Objet_U::associer_(Objet_U&): associates an object with the coupled problem, checking the type dynamically.
362 *
363 * The object can be:
364 * - a time scheme (Schema_Temps_base), associated with the problems
365 * - a problem (Probleme_base), added to the list
366 *
367 * @param (Objet_U& ob) the object to associate
368 * @return (int) 1 if the association succeeded, 0 otherwise
369 * @throws if the object is not of an expected type
370 */
372{
373 if( sub_type(Schema_Temps_base, ob))
374 {
376 return 1;
377 }
378 else if( sub_type(Probleme_base, ob))
379 {
380 Probleme_base& pb = ref_cast(Probleme_base, ob);
381 ajouter(pb);
382 return 1;
383 }
384 else
385 return 0;
386}
387
388/*! @brief Associates a copy of the time scheme with each problem of the coupled problem.
389 *
390 * @param (Schema_Temps_base& sch) the time scheme to associate
391 */
393{
394 sch_clones.dimensionner(nb_problemes());
395 for (int i=0; i<nb_problemes(); i++)
396 {
397 sch_clones[i]=sch; // Clone the scheme
398 Probleme_base& pb=ref_cast(Probleme_base,probleme(i));
399 pb.associer_sch_tps_base(sch_clones[i].valeur()); // association
400 //We assign the value 1 to schema_impr_ for problem 0's scheme
401 //and 0 for the others. Only one scheme should print.
402 if (i!=0) pb.schema_temps().schema_impr()=0;
403 }
404}
405/*! @brief Returns the time scheme associated with the coupled problems (const version).
406 *
407 * @return (Schema_Temps_base&) the associated time scheme
408 */
410{
411 if (nb_problemes()==0)
412 {
413 Cerr << "You forgot to associate problems to the coupled problem named " << le_nom() << finl;
415 }
416 const Probleme_base& pb=ref_cast(Probleme_base,probleme(0));
417 return pb.schema_temps();
418}
419
420/*! @brief Returns the time scheme associated with the coupled problems.
421 *
422 * @return (Schema_Temps_base&) the associated time scheme
423 */
425{
426 if (nb_problemes()==0)
427 {
428 Cerr << "You forgot to associate problems to the coupled problem named " << le_nom() << finl;
430 }
431 Probleme_base& pb=ref_cast(Probleme_base,probleme(0));
432 return pb.schema_temps();
433}
434
435/*! @brief Associates a discretization with all problems of the coupled problem.
436 *
437 * Calls Probleme_Base::discretiser(const Discretisation_base&)
438 * on each of the sub-problems of the coupled problem.
439 * see Probleme_Base::discretiser(const Discretisation_base&)
440 *
441 * @param (Discretisation_base& dis) a discretization for all problems
442 */
444{
445 // Loop to discretize each problem of the coupled problem:
446 for(int i=0; i< nb_problemes(); i++)
447 {
448 Cerr<<"The problem that we are starting to discretize is "<<probleme(i).le_nom()<<finl;
449 Probleme_base& pb=ref_cast(Probleme_base,probleme(i));
450 pb.discretiser(dis);
451 }
452}
453
455{
456 Ecrire_YAML yaml_file;
457 bool pdi_format = false;
458 Nom yaml_fname;
459 for (int i=0; i<nb_problemes(); i++)
460 {
461 const Probleme_base& pb=ref_cast(Probleme_base,probleme(i));
462 const Nom& format = pb.checkpoint_format();
463 if(Motcle(format) == "pdi")
464 {
465 if(i>0 && pb.yaml_filename() != yaml_fname)
466 {
467 Cerr << "Probleme_Couple::sauver() Error! You have provided different yaml files for each of your problems to initialize PDI. It has to be the same. " << finl;
469 }
470 yaml_fname = pb.yaml_filename();
471 const Nom& fname = pb.checkpoint_filename();
472 yaml_file.add_pb_base(pb, fname);
473 pdi_format = true;
474 }
475 }
476 // we need to initialize PDI with a yaml file that contains the information of all the problems with a PDI checkpoint format
477 // (allows to initialize PDI once for all checkpoints and not multiple times)
478 if(pdi_format && !TRUST_2_PDI::is_PDI_initialized())
479 {
480 if(yaml_fname == "??")
481 {
482 yaml_fname = Nom("save_") + le_nom() + Nom(".yml");
483 yaml_file.write_checkpoint_file(yaml_fname.getString());
484 }
485 TRUST_2_PDI::init(yaml_fname.getString());
486 }
487
488 for(int i=0; i<nb_problemes(); i++)
489 ref_cast(Probleme_base,probleme(i)).sauver();
490
491}
class Couplage_U
Definition Couplage_U.h:31
const Probleme_U & probleme(int i) const
Definition Couplage_U.h:127
void initialize() override
This method is called once at the beginning, before any other one of the interface Problem.
bool initTimeStep(double dt) override
This method allocates and initializes the unknown and given fields for the future time step.
int limpr() const override
Should we print the execution statistics now?
void addProblem(Probleme_U &)
Definition Couplage_U.h:112
int nb_problemes() const
Definition Couplage_U.h:117
class Discretisation_base This class represents a spatial discretization scheme, which
virtual int calculer_coeffs_echange(double temps)
Computes the exchange coefficients for thermally coupled problems.
virtual void mettre_a_jour(double temps, Domaine_dis_base &, Probleme_base &)
class Ecrire_YAML Use this to generate a yaml file that will then be read by the PDI library (for che...
Definition Ecrire_YAML.h:64
void write_checkpoint_file(const std::string &yaml_fname)
Generate the YAML file that will be read for checkpoint.
void add_pb_base(const Probleme_base &pb_base, const Nom &full_file_name)
Definition Ecrire_YAML.h:76
Class defining operators and methods for all reading operation in an input flow (file,...
Definition Entree.h:42
virtual Domaine_Cl_dis_base & domaine_Cl_dis()
Returns the discretized boundary condition domain associated with the equation.
static Objet_U & objet(const Nom &)
See Interprete_bloc::objet_global(). BM: the Interprete class is not the best place for this.
A character string (Nom) in uppercase.
Definition Motcle.h:26
class Nom: a character string for naming TRUST objects.
Definition Nom.h:31
Nom & prefix(const char *const)
Definition Nom.cpp:324
const std::string & getString() const
Definition Nom.h:92
const Nom & que_suis_je() const
Returns the string identifying the class.
Definition Objet_U.cpp:104
virtual Entree & readOn(Entree &)
Reads an Objet_U from an input stream. Virtual method to override.
Definition Objet_U.cpp:289
Objet_U()
Default constructor: assigns a unique identifier to the object (object_id_) and registers the object ...
Definition Objet_U.cpp:54
virtual Sortie & printOn(Sortie &) const
Writes the object to an output stream. Virtual method to override.
Definition Objet_U.cpp:278
bool solveTimeStep() override
For possible re-implementation and unified call from python.
Probleme_Couple This is the historical coupling class of TRUST.
virtual void discretiser(Discretisation_base &)
Associates a discretization with all problems of the coupled problem.
void sauver() const override
Save the problem state to disk.
bool initTimeStep(double dt) override
This method allocates and initializes the unknown and given fields for the future time step.
void initialize() override
This method is called once at the beginning, before any other one of the interface Problem.
double computeTimeStep(bool &stop) const override
Compute the value the Problem would like for the next time step.
virtual void associer_sch_tps_base(Schema_Temps_base &)
Associates a copy of the time scheme with each problem of the coupled problem.
int associer_(Objet_U &) override
Overrides Objet_U::associer_(Objet_U&): associates an object with the coupled problem,...
bool updateGivenFields() override
WARNING:
bool solveTimeStep() override
For possible re-implementation and unified call from python.
virtual const Schema_Temps_base & schema_temps() const
Returns the time scheme associated with the coupled problems (const version).
void ajouter(Probleme_base &)
Adds a problem to the list of coupled problems.
bool iterateTimeStep(bool &converged) override
In the case solveTimeStep uses an iterative process, this method executes a single iteration.
const Nom & le_nom() const override
Returns the name of the Objet_U. Virtual method to override: returns "neant" in this implementation.
Definition Probleme_U.h:109
virtual bool iterateTimeStep(bool &converged)
In the case solveTimeStep uses an iterative process, this method executes a single iteration.
virtual bool solveTimeStep()
For possible re-implementation and unified call from python.
virtual bool updateGivenFields()
WARNING:
virtual double computeTimeStep(bool &stop) const
Compute the value the Problem would like for the next time step.
class Probleme_base It is a Probleme_U that is not a coupling.
virtual void discretiser(Discretisation_base &)
Assigns a discretization to the problem Discretizes the Domain associated with the problem with the d...
const Nom & checkpoint_filename() const
void associer_pb_couple(const Probleme_Couple &pbc)
const Nom & checkpoint_format() const
const Domaine & domaine() const
Returns the domain associated with the problem.
bool updateGivenFields() override
WARNING:
const Nom & yaml_filename() const
virtual void associer_sch_tps_base(const Schema_Temps_base &)
Associates a time scheme with the problem.
const Schema_Temps_base & schema_temps() const
Returns the time scheme associated with the problem.
virtual int nombre_d_equations() const =0
virtual const Equation_base & equation(int) const =0
const Domaine_dis_base & domaine_dis() const
Returns the discretized domain associated with the problem (const version).
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
virtual int faire_un_pas_de_temps_pb_couple(Probleme_Couple &, int &ok)
class Schema_Temps_base
double temps_courant() const
Returns the current time.
const double & residu() const
int stationnaire_atteint() const
double facteur_securite_pas() const
Returns the safety factor or multiplier of delta_t.
Base class for output streams.
Definition Sortie.h:52
static void init(std::string IO_config)
Definition TRUST_2_PDI.h:63
static int is_PDI_initialized()