TrioCFD 1.9.9_beta
TrioCFD documentation
Loading...
Searching...
No Matches
Equation_base.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 <Parametre_diffusion_implicite.h>
17#include <Solveur_Implicite_base.h>
18#include <Schema_Euler_explicite.h>
19#include <Schema_Euler_Implicite.h>
20#include <Source_dep_inco_base.h>
21#include <Operateur_Conv_base.h>
22#include <Op_Conv_negligeable.h>
23#include <Operateur_Diff_base.h>
24#include <Discretisation_base.h>
25#include <Matrice_Morse_Diag.h>
26#include <Frontiere_dis_base.h>
27#include <Matrice_Morse_Sym.h>
28#include <Operateur_base.h>
29#include <TRUSTTab_parts.h>
30#include <EcrFicPartage.h>
31#include <Postraitement.h>
32#include <Equation_base.h>
33#include <Milieu_base.h>
34#include <TRUST_2_PDI.h>
35#include <Domaine_VF.h>
36#include <SolveurSys.h>
37#include <Domaine_VF.h>
38#include <Operateur.h>
39#include <EChaine.h>
40#include <Avanc.h>
41#include <Debog.h>
42#include <Param.h>
43#include <Perf_counters.h>
44
45// XD condinit objet_lecture nul NO_BRACE Initial condition.
46// XD attr nom chaine nom REQ Name of initial condition field.
47// XD attr ch field_base ch REQ Type field and the initial values.
48
49// XD condinits listobj condinits INHERITS_BRACE condinit NO_COMMA Initial conditions.
50
51Implemente_base_sans_constructeur(Equation_base,"Equation_base",Objet_U);
52// XD eqn_base mor_eqn eqn_base BRACE Basic class for equations.
53/* Attributes further down in the cpp: */
54
55Equation_base::Equation_base()
56{
58 implicite_=-1;
59 has_time_factor_= false;
60 Nom expr_equation_non_resolue="0";
61 equation_non_resolue_.setNbVar(1);
62 equation_non_resolue_.setString(expr_equation_non_resolue); // Default value, equation solved
63 equation_non_resolue_.addVar("t");
64 equation_non_resolue_.parseString();
66}
67
69{
70 std::string str= equation_non_resolue_.getString();
71 std::string str2("INPUT_INT_VALUE");
72 if (str.compare(str2)==0)
73 {
74 Nom name = eq_non_resolue_input_.getName();
75 int eq_non_resolue_int = mon_probleme->getOutputIntValue(name);
76 return eq_non_resolue_int;
77 }
78 else
79 {
80 double t = le_schema_en_temps->temps_courant();
81 equation_non_resolue_.setVar("t",t);
82 return (int)equation_non_resolue_.eval();
83 }
84}
85
86/*! @brief Returns the discretized domain associated with the equation.
87 *
88 * @return (Domaine_dis_base&) the discretized domain associated with the equation
89 * @throws the discretized domain object (Domaine_dis) is invalid,
90 * associated problem not discretized.
91 */
93{
94 if (!le_dom_dis)
95 {
96 Cerr << "There is no object of type Domaine_dis yet associated to the equation " << que_suis_je() << finl;
97 Cerr << "This means that the problem has not been discretized or" << finl;
98 Cerr << "that instruction Discretiser is misplaced" << finl;
99 exit();
100 }
101 return le_dom_dis.valeur();
102}
103
104/*! @brief Returns the discretized domain associated with the equation.
105 *
106 * (const version)
107 *
108 * @return (Domaine_dis_base&) the discretized domain associated with the equation
109 * @throws the discretized domain object (Domaine_dis) is invalid,
110 * associated problem not discretized.
111 */
113{
114 if (!le_dom_dis)
115 {
116 Cerr << "There is no object of type Domaine_dis associated to the equation " << que_suis_je() << finl;
117 Cerr << "This means that the problem has not been discretized" << finl;
118 exit();
119 }
120 assert(le_dom_dis);
121 return le_dom_dis.valeur();
122}
123
124/*! @brief Completes the construction (initialization) of objects associated with the equation.
125 *
126 * Completes the sources, associates the equation with the unknown, completes the
127 * operators, completes the discretized boundary conditions.
128 * See the methods Source_base::completer(),
129 * Operateur_base::completer()
130 * Domaine_Cl_dis_base::completer()
131 * Domaine_Cl_dis_base::completer(const Domaine_dis_base& )
132 *
133 */
135{
136 inconnue().associer_eqn(*this);
137 if (le_dom_Cl_dis)
138 le_dom_Cl_dis->completer();
139
140 inconnue().associer_domaine_cl_dis(le_dom_Cl_dis);
141
142 if (mon_probleme->is_coupled())
143 {
144 bool err = false;
145 int nb_cl = le_dom_Cl_dis->nb_cond_lim();
146 for (int i=0; i<nb_cl; i++)
147 {
148 const Cond_lim_base& la_cl = le_dom_Cl_dis->les_conditions_limites(i);
149 const Frontiere& la_frontiere = la_cl.frontiere_dis().frontiere();
150
151 bool raccord_found = false;
152
153 // VDF and PolyMAC_HFV
154 if (la_cl.que_suis_je().debute_par("Paroi_Echange_contact") || la_cl.que_suis_je().debute_par("Echange_contact_Rayo"))
155 raccord_found = true;
156
157 // VEF
158 if (la_cl.que_suis_je().debute_par("Scalaire_impose_paroi") || la_cl.que_suis_je().debute_par("paroi_temperature_imposee_rayo") || la_cl.que_suis_je().debute_par("temperature_imposee_paroi"))
159 raccord_found = true;
160
161 if ( raccord_found && !sub_type(Raccord_base,la_frontiere))
162 {
163 Cerr << "====================================================================" << finl;
164 Cerr << "Boundary " << la_frontiere.le_nom() << " should be a raccord" << finl;
165 Cerr << "Add in your data file between the definition and the partition of the domain " << mon_probleme->domaine().le_nom() << " : " << finl;
166 Cerr << "Modif_bord_to_raccord " << mon_probleme->domaine().le_nom() << " " << la_frontiere.le_nom() << finl;
167 err = true;
168 }
169 }
170 if(err)
171 exit();
172 }
173
174 //Addition of an empty element that will be returned if no model is found
175 RefObjU mod;
176 if (liste_modeles_.size()==0)
177 liste_modeles_.add(mod);
178
179 // for equations not calling preparer_calcul
181 int nb_op=nombre_d_operateurs();
182 Nom msg="Equation_base::completer(), nb_op = " ;
183 Debog::verifier(msg, nb_op);
184 for(int i=0; i<nb_op; i++)
185 operateur(i).completer();
186
187 if (solveur_masse) // [ABN]: In case of Front-Tracking, mass solver mass might be uninitialized ...
189
190 les_sources.completer();
192
193 // Determine implicite_ and sys_invariant_ variables:
194 implicite_=0;
195 for(int i=0; i<nombre_d_operateurs(); i++)
196 {
197 if(operateur(i).l_op_base().get_decal_temps()==1)
198 {
199 ++implicite_;
201 }
202 }
203}
204
205
206/*! @brief Overrides Objet_U::printOn: prints the equation and its components to an output stream.
207 *
208 * Prints the equation name, the mass solver, the source terms,
209 * the discretized boundary conditions, the unknowns, and the operators.
210 *
211 * @param (Sortie& os) an output stream
212 * @return (Sortie&) the modified output stream
213 */
215{
216 os << nom_ << finl;
217 os << solveur_masse;
218 os << les_sources;
219 os << le_dom_Cl_dis;
220 os << inconnue() ;
221 for(int i=0; i<nombre_d_operateurs(); i++)
222 {
223 os << operateur(i).l_op_base();
224 }
225 return os;
226}
227
228/*! @brief Reads an equation from an input stream.
229 *
230 * The expected format is:
231 * {
232 * [Source { [sou_1], [sour_2], ...} ]
233 * Conditions_limites { [cl_1] [cl_2] ... }
234 * Conditions_initiales { [cl_init] }
235 * }
236 *
237 * @param (Entree& is) an input stream to read the equation from
238 * @return (Entree&) the modified input stream
239 * @throws bad read format, opening brace expected
240 * @throws no initial conditions, you must initialize the unknown
241 * @throws no boundary conditions, you must provide boundary conditions
242 */
244{
245 Cerr<<"Reading of data for a "<<que_suis_je()<<" equation"<<finl;
246 Param param(que_suis_je());
247 set_param(param);
248 Nom expr_equation_non_resolue="0";
249 param.ajouter("disable_equation_residual",&disable_equation_residual_); // XD attr disable_equation_residual entier disable_equation_residual OPT The equation residual will not be used for the problem residual used when checking time convergence or computing dynamic time-step
250 equation_non_resolue_.setString(expr_equation_non_resolue);
251 param.lire_avec_accolades_depuis(is);
252 matrice_init = 0;
253 return is;
254}
255
256// XD attr convection bloc_convection convection OPT Keyword to alter the convection scheme.
257// XD attr diffusion bloc_diffusion diffusion OPT Keyword to specify the diffusion operator.
259{
260 param.ajouter_non_std("conditions_limites|boundary_conditions",(this),Param::REQUIRED); // XD attr conditions_limites|boundary_conditions condlims conditions_limites OPT Boundary conditions.
261 param.ajouter_non_std("conditions_initiales|initial_conditions",(this),Param::REQUIRED); // XD attr conditions_initiales|initial_conditions condinits conditions_initiales OPT Initial conditions.
262 param.ajouter_non_std("sources",(this)); // XD attr sources sources sources OPT To introduce a source term into an
263 // XD_CONT equation (in case of several source terms into the same equation, the blocks corresponding to the various
264 // XD_CONT terms need to be separated by a comma)
265 param.ajouter_non_std("ecrire_fichier_xyz_valeur",(this)); // XD attr ecrire_fichier_xyz_valeur ecrire_fichier_xyz_valeur ecrire_fichier_xyz_valeur OPT This keyword is used to write the values of a field only for some boundaries in a text file
266 param.ajouter("parametre_equation",&parametre_equation_); // XD attr parametre_equation parametre_equation_base parametre_equation OPT Keyword used to specify additional parameters for the equation
267 param.ajouter_non_std("equation_non_resolue",(this)); // XD attr equation_non_resolue chaine equation_non_resolue OPT
268 // XD_CONT The equation will not be solved while condition(t) is verified if equation_non_resolue keyword is used.
269 // XD_CONT Exemple: The Navier-Stokes equations are not solved between time t0 and t1. NL2 Navier_Sokes_Standard NL2 {
270 // XD_CONT equation_non_resolue (t>t0)*(t<t1) }
271 param.ajouter_non_std("rename_equation|renommer_equation",(this)); // XD attr renommer_equation chaine rename_equation OPT Rename the equation with a specific name.
272}
273
275{
276 if (mot=="sources")
277 {
278 lire_sources(is);
279 return 1;
280 }
281 else if (mot=="conditions_limites|boundary_conditions")
282 {
283 lire_cl(is);
284 verif_Cl();
285 return 1;
286 }
287 else if (mot=="conditions_initiales|initial_conditions")
288 {
289 lire_cond_init(is);
290 return 1;
291 }
292 else if (mot=="ecrire_fichier_xyz_valeur")
293 {
294 xyz_field_values_file_.associer_eqn(*this);
295 is >> xyz_field_values_file_;
296 return 1;
297 }
298 else if (mot=="rename_equation|renommer_equation")
299 {
300 Cerr << "Equation " << nom_ << " renamed to : ";
301 is >> nom_;
302 Cerr << nom_ << finl;
303 return 1;
304 }
305 else if (mot=="equation_non_resolue")
306 {
307 // differentiate an entry with an expression from an entry with the keyword eq_non_resoue_input_
308 // On complete:
309 Motcle expr_equation_non_resolue;
310 is >> expr_equation_non_resolue;
311 equation_non_resolue_.setString(expr_equation_non_resolue);
312 if (expr_equation_non_resolue=="INPUT_INT_VALUE")
313 is >> eq_non_resolue_input_;
314 else
315 equation_non_resolue_.parseString();
316 return 1;
317 }
318 return -1;
319}
320
321
322/*! @brief Reading of source terms in an input stream.
323 *
324 * @param (Entree& is) input stream to read source terms
325 * @return (Entree&) the modified input stream
326 */
328{
329 static bool already_read=false;
330 if (already_read)
331 {
332 Cerr << "Error: the 'sources' keyword should only appear once per equation." << finl;
333 Cerr << "To declare several source terms, list them inside a single block separated by commas:" << finl;
334 Cerr << " sources { TermA { ... } , TermB { ... } }" << finl;
335 // Process::exit(); // comment that out since it appears pb_multiphase is doing shadowy stuff...
336 }
337
338 already_read=true;
339 Cerr << "Reading of source terms" << finl ;
340 is >> les_sources;
341 return is;
342}
343
344/*! @brief Returns the source terms associated with the equation
345 *
346 * @return (Sources&) the list of source terms associated with the equation
347 */
352
353/*! @brief Returns the source terms associated with the equation (const version)
354 *
355 * @return (Sources&) the list of source terms associated with the equation
356 */
358{
359 return les_sources;
360}
361
362/*! @brief Reading of initial conditions in an input stream.
363 *
364 * The reading format is as follows:
365 * {
366 * Name [MUST BE THE NAME OF THE UNKNOWN]
367 * [READ A GIVEN FIELD]
368 * }
369 *
370 * @param (Entree& is) input stream
371 * @return (Entree&) the modified input stream
372 * @throws format error, opening brace expected
373 * @throws bad name for the unknown
374 * @throws format error, closing brace expected
375 */
377{
378 Cerr << "Reading of initial conditions\n";
379 Nom nom;
380 Motcle motlu;
381 is >> nom;
382 motlu = nom;
383 if(motlu!=Motcle("{"))
384 {
385 Cerr << "We expected a { while reading " << que_suis_je() << finl;
386 Cerr << "and not : " << nom << finl;
387 exit();
388 }
389 is >> nom;
390 motlu = nom;
391 if (motlu != Motcle(inconnue().le_nom()))
392 {
393 Cerr << nom << " is not the name of the unknown "
394 << inconnue().le_nom() << finl;
395 exit();
396 }
397 OWN_PTR(Champ_Don_base) ch_init;
398 is >> ch_init;
399 const int nb_comp = ch_init->nb_comp();
401
402 //Cerr<<"inconnue().que_suis_je() = "<<inconnue().que_suis_je()<<finl;
403
404 inconnue().affecter(ch_init.valeur());
405 is >> nom;
406 motlu = nom;
407 if(motlu!=Motcle("}"))
408 {
409 Cerr << "We expected a } while reading " << que_suis_je() << finl;
410 Cerr << "and not : " << nom << finl;
411 exit();
412 }
413 return is;
414}
415
416/*! @brief Reading of boundary conditions in an input stream.
417 *
418 * see Domaine_Cl_dis_base::readOn
419 *
420 * @param (Entree& is) input stream
421 * @return (Entree&) the modified input stream
422 * @throws the discretized boundary condition domain is empty
423 */
425{
426 Cerr << "Reading of boundaries conditions\n";
427 if(!le_dom_Cl_dis)
428 {
429 Cerr << "Error while reading boundaries conditions : " <<
430 que_suis_je() << finl;
431 Cerr << "The Domaine_Cl_dis_base is empty ..." << finl;
432 exit();
433 }
434 is >> le_dom_Cl_dis.valeur();
435 return is;
436}
437
438/*! @brief We save the unknown, then the source terms to an output stream.
439 *
440 * @param (Sortie& os)
441 * @return (int) the return code of Champ_Inc::sauvegarder()
442 */
444{
445 int ret = inconnue().sauvegarder(os);
446 for (int i = 0; i < les_sources.size(); i++)
447 ret += les_sources(i)->sauvegarder(os);
448 return ret;
449}
450
451
452/*! @brief for PDI IO: retrieve name, type and dimensions of the data to save/restore.
453 * This has to be overrode for all the equations that either:
454 * - have extra fields (ie in addition to the unknown) or extra scalars to save/restore.
455 * - want to save the unknown but with a different name
456 * These data will then be written in a YAML file, to initialize PDI.
457 * They have to be shared with PDI afterwards, when they need to be read/written (via TRUST_2_PDI)
458 *
459 */
460std::vector<YAML_data> Equation_base::data_a_sauvegarder() const
461{
462 std::vector<YAML_data> data = inconnue().data_a_sauvegarder();
463 return data;
464}
465
466/*! @brief We resume the unknown from an input stream.
467 *
468 * [WE SEARCH FOR THE UNKNOWN BY ITS NAME]
469 * [WE READ THE UNKNOWN]
470 * See Champ_Inc::reprendre(Entree&)
471 *
472 * @param (Entree& fich) input stream (file) to read
473 * @return (int) always returns 1
474 * @throws restart error, end of file reached without finding the unknown
475 */
477{
479 {
480 double temps = schema_temps().temps_courant();
481 Nom field_tag(inconnue().le_nom());
482 field_tag += inconnue().que_suis_je();
483 field_tag += probleme().domaine().le_nom();
484 field_tag += Nom(temps,probleme().reprise_format_temps());
485 // This part ensure the backwards compatibility of TRUST regarding the renaming of the PolyMAC family of discretisation. It will be deprecated in the TRUST 2.0
486
487 if (probleme().discretisation().is_poly_family())
488 {
489 Nom field_tag_syno = create_polymacfamily_syno(field_tag);
490 avancer_fichier_with_syno(fich,field_tag,field_tag_syno);
491 }
492 // end of the backward compatibility
493 else
494 avancer_fichier(fich,field_tag);
495 }
496 int ret = inconnue().reprendre(fich);
497 for (int i = 0; i < les_sources.size(); i++)
498 ret = ret && les_sources(i)->reprendre(fich);
499 return ret;
500}
501
502/*! @brief Create a synonym of a field name in order to ensure backward compatibility with old names of the PolyMAC discretisation family
503 *
504 * @param field_tag
505 * @return synonym of the field tag
506 */
508{
509 Nom field_tag_syno=field_tag;
510 auto create_syno= [field_tag](std::string pattern, std::string replace)
511 {
512 std::string s = field_tag.getString();
513 std::size_t pos = s.find(pattern);
514 while (pos != std::string::npos)
515 {
516 s.replace(pos, pattern.length(), replace);
517 pos = s.find(pattern, pos + replace.length());
518 }
519 return Nom(s);
520 };
521
522 if (probleme().discretisation().is_PolyMAC_CDO())
523 field_tag_syno=create_syno("PolyMAC_CDO","PolyMAC");
524 else if (probleme().discretisation().is_PolyMAC_MPFA())
525 field_tag_syno=create_syno("PolyMAC_MPFA","PolyMAC_P0");
526 else if (probleme().discretisation().is_PolyMAC_HFV())
527 field_tag_syno=create_syno("PolyMAC_HFV","PolyMAC_P0P1NC");
528 return field_tag_syno;
529}
530/*! @brief Ask the time scheme if an output is needed.
531 *
532 * Returns 1 if an output is needed.
533 * Simple call to Schema_Temps_base::limpr()
534 *
535 * @return (int) 1 if an output is needed, 0 otherwise.
536 */
538{
539 return le_schema_en_temps->limpr();
540}
541
542/*! @brief Prints the equation operators to an output stream, unconditionally.
543 *
544 * calls Operateur_base::impr(os)
545 *
546 * @param (Sortie& os) output stream
547 * @return (int) always returns 1
548 */
550{
551 for(int i=0; i<nombre_d_operateurs(); i++)
552 operateur(i).impr(os);
553 //if (je_suis_maitre() && les_sources.size()>0) os << finl << "Printing source terms for equation " << que_suis_je() << " : " << finl;
554 les_sources.impr(os);
555 return 1;
556}
557
558/*! @brief Prints the equation operators if the time scheme indicates it is necessary.
559 *
560 * [IF limpr() THEN impr(os)]
561 *
562 * @param (Sortie& os) output stream
563 */
565{
566 xyz_field_values_file_.write_fields();
567 if (limpr() )
568 impr(os);
569}
570
571/*! @brief Returns the time derivative of the unknown I of the equation: dI/dt = M-1*(sum(operators(I) + sources))
572 *
573 * In : derivee contains I (but immediatly set to 0)
574 * Out: derivee contains dI/dt
575 *
576 */
577DoubleTab& Equation_base::derivee_en_temps_inco(DoubleTab& derivee)
578{
579 derivee = 0.;
580
581 DoubleTrav secmem(derivee);
582 // secmem = sum(operators) + sources + equation specific terms
583
584
585 derivee_en_temps_inco_sources(secmem);
586 const double time_factor = get_time_factor();
587
588 bool calcul_explicite = false;
589 if (parametre_equation_ && sub_type(Parametre_implicite, parametre_equation_.valeur()))
590 {
591 Parametre_implicite& param2 = ref_cast(Parametre_implicite, parametre_equation_.valeur());
592 calcul_explicite = param2.calcul_explicite();
593 }
594
595 if (schema_temps().diffusion_implicite() && !calcul_explicite)
596 {
597 // Add convection operator only if equation has one
598 derivee=inconnue().valeurs();
599 if (nombre_d_operateurs()>1)
600 {
601 derivee_en_temps_conv(secmem, derivee);
603 {
604 secmem *= time_factor;
605 }
606 }
607 }
608 else
609 {
610 verify_scheme();
611
612 // Add all explicit operators
613 for(int i=0; i<nombre_d_operateurs(); i++)
614 if(operateur(i).l_op_base().get_decal_temps()!=1)
615 {
617 {
618 DoubleTrav secmem_tmp(secmem);
619 operateur(i).ajouter(secmem_tmp);
620 if (i == 1) secmem_tmp *= time_factor;
621 secmem += secmem_tmp;
622 }
623 else
624 {
625 operateur(i).ajouter(secmem);
626 }
627 }
628 }
629 les_sources.ajouter(secmem);
631 {
632 // Store dI/dt(n) = M-1 secmem :
633 derivee_en_temps().valeurs()=secmem;
634 solveur_masse->appliquer(derivee_en_temps().valeurs());
635 schema_temps().modifier_second_membre((*this),secmem); // Change secmem for some schemes (eg: Adams_Bashforth)
636 }
637
638 corriger_derivee_expl(secmem); // Add specific term for an equation (eg: -gradP for Navier Stokes)
639
640 if (implicite_==0)
641 {
642 solveur_masse->appliquer(secmem); // M-1 * secmem
643 if (schema_temps().diffusion_implicite() && !calcul_explicite)
644 {
645 // Solve: (1/dt + M-1*A)*dI = M-1 * secmem
646 // where A is the diffusion
648 }
649 else
650 {
651 derivee = secmem;
652 derivee.echange_espace_virtuel();
653 }
654 corriger_derivee_impl(derivee); // Solve specific implicit term for an equation (eg: pressure for Navier Stokes)
655
656 }
657 else if (implicite_>0)
658 {
659 // TRUST support notices that this part has never been covered...
660 //implicit
661 // M dU/dt + AU* = f -BUn;
662 // U* = Un+dt dU/dt
663 // (M/dt + A) U* = f -BUn + M/dt Un
664 //
665 double dt=schema_temps().pas_de_temps();
666 for(int i=0; i<nombre_d_operateurs(); i++)
667 {
668 //loop over operators
670 if(!op.get_matrice())
671 op.set_matrice().typer("Matrice_Morse");
672 if(op.get_decal_temps()==1)
673 {
674 //if (op.set_matrice()->nb_lignes()<2)
675 {
676 Matrice_Morse& matrice=ref_cast(Matrice_Morse,op.set_matrice().valeur());
677 op.dimensionner(matrice);
679 }
680 if(!sys_invariant_)
681 {
682 Matrice_Morse& matrice=ref_cast(Matrice_Morse, op.set_matrice().valeur());
683 op.contribuer_a_avec(inconnue().valeurs(), matrice);
684 solv_masse().ajouter_masse(dt, op.set_matrice().valeur());
685
686 if(
687 (op.get_solveur()->que_suis_je()=="Solv_Cholesky")
688 ||
689 (op.get_solveur()->que_suis_je()=="Solv_GCP")
690 )
691 {
692 Matrice_Morse_Sym new_mat(matrice);
693 new_mat.set_est_definie(1);
694 op.set_matrice()=new_mat;
695 ref_cast_non_const(SolveurSys,op.get_solveur())->reinit();
696 }
697 }
698 }
699 }
700 if(implicite_==1)
701 {
702 // Single implicit operator.
703 // Assumed to be the first one (diffusion!!)
705 Matrice_Base& matrice=op.set_matrice().valeur();
706 // DoubleTrav secmem(derivee);
707 secmem=derivee;
708 solv_masse().ajouter_masse(dt, secmem, inconnue().valeurs());
709 op.contribuer_au_second_membre(secmem );
710 op.set_solveur().resoudre_systeme(matrice,
711 secmem,
712 derivee
713 );
714 solv_masse().corriger_solution(derivee,inconnue().valeurs());
715
716 derivee-=inconnue().valeurs();
717 derivee/=dt;
718
719 //Used only to compute boundary fluxes when diffusion is made implicit!
720 DoubleTab resu;
721 resu=derivee;
722 operateur(0).calculer(inconnue().valeurs(), resu);
723 }
724 else
725 {
726 // multiple implicit operators ...
727 Cerr << "Must be coded ... " << finl;
728 exit();
729 }
730 }
731 else
732 {
733 Cerr << "Error in Equation_base::derivee_en_temps_inco" << finl;
734 Cerr << "implicite_ = " << implicite_ << " has not been initialized!" << finl;
735 Cerr << "May be " << que_suis_je() << "::completer() method doesn't call Equation_base::completer()" << finl;
736 Cerr << "Contact TRUST support." << finl;
738 }
739
740 return derivee;
741}
742
743/*! @brief Returns the problem associated with the equation.
744 *
745 * @return (Probleme_base&) the problem associated with the equation
746 */
748{
749 return mon_probleme.valeur();
750}
751
752/*! @brief Returns the problem associated with the equation.
753 *
754 * (const version)
755 *
756 * @return (Probleme_base&) the problem associated with the equation
757 */
759{
760 return mon_probleme.valeur();
761}
762
763/*! @brief Method called when the object instance is created in the data file (Interprete::ajouter)
764 *
765 */
766void Equation_base::nommer(const Nom& un_nom)
767{
768 nom_ = un_nom;
769}
770
771/*! @brief Associates with the Problem passed as parameter.
772 *
773 * Also associates the sources, operators and mass
774 * solver with the equation.
775 *
776 * @param (Probleme_base& pb) the problem with which the equation must be associated
777 */
779{
780 mon_probleme=pb;
781 // Modif B. Mathieu: for front_tracking, the equation name
782 // is given in the data file during object instantiation
783 // (the "nommer" method is called at that moment).
784 // If the equation is not instantiated, the name is empty, we give
785 // the default name which is defined below:
786 Nom nom_vide;
787 if (nom_ == nom_vide)
788 {
789 nom_ = pb.le_nom();
790 nom_ += que_suis_je();
791 }
792 // end modif B.M.
793 les_sources.associer_eqn(*this);
794 int nb_op = nombre_d_operateurs();
795 for(int i=0; i<nb_op; i++)
796 operateur(i).associer_eqn(*this);
797
798}
799
800/*! @brief Discretizes the equation.
801 *
802 * Type the domaine_Cl_dis, the format, associate it with the equation.
803 * Type the mass solver, associate the discretized domain and
804 * the discretized boundary condition domain with it.
805 *
806 */
808{
809 Cerr << "Discretizing of the boundary conditions... ";
810 le_dom_Cl_dis.typer(discretisation().domaine_cl_dis_type());
811 le_dom_Cl_dis->associer(domaine_dis());
812 Cerr << " OK " << finl;
813
814 le_dom_Cl_dis->associer_eqn(*this);
815 le_dom_Cl_dis->associer_inconnue(inconnue());
816
817 /*
818 * XXX : Elie Saikali : to type correctly the mass solver ...
819 */
820 Nom typ = discretisation().get_name_of_type_for("Solveur_Masse", "??" /* rien */, *this);
821 solveur_masse.typer(typ);
822 solveur_masse->associer_eqn(*this);
823 solveur_masse->associer_domaine_dis_base(domaine_dis());
824 solveur_masse->associer_domaine_cl_dis_base(le_dom_Cl_dis.valeur());
825
827 {
828 Motcle directive("temperature");
829 if (inconnue().is_vectorial())
830 directive="vitesse";
831
832 Nom nom("derivee_en_temps_");
833 nom += inconnue().le_nom();
834
835 Nom unite(inconnue().unites()[0]);
836 unite += "/s";
837
838 discretisation().discretiser_champ(directive,domaine_dis(),nom,unite,
839 inconnue().nb_comp(),
840 schema_temps().nb_valeurs_temporelles(),
841 schema_temps().temps_courant(),derivee_en_temps_);
842
843 champs_compris_.ajoute_champ(derivee_en_temps_);
844 }
845}
850
851/*! @brief Associates the time scheme with the equation.
852 *
853 * @param (Schema_Temps_base& un_schema_en_temps) the time scheme to associate with the equation
854 */
856{
857 le_schema_en_temps=un_schema_en_temps;
858}
859
860/*! @brief Returns the time scheme associated with the equation.
861 *
862 * @return (Schema_Temps_base&) the time scheme associated with the equation
863 * @throws no time scheme associated with the equation
864 */
866{
867 if(!le_schema_en_temps)
868 {
869 Cerr << "Error : " << que_suis_je()
870 << "has not been associated to a time scheme " << finl;
871 exit();
872 }
873 return le_schema_en_temps.valeur();
874}
875
876/*! @brief Returns the time scheme associated with the equation.
877 *
878 * @return (Schema_Temps_base&) the time scheme associated with the equation
879 * @throws no time scheme associated with the equation
880 */
882{
883 if(!le_schema_en_temps)
884 {
885 Cerr << "Error : " << que_suis_je()
886 << "has not been associated to a time scheme " << finl;
887 exit();
888 }
889 return le_schema_en_temps.valeur();
890}
891
892/*! @brief Associates the discretized domain with the equation.
893 *
894 * @param (Domaine_dis_base& z) the discretized domain to associate
895 */
897{
898 le_dom_dis=z;
899}
900
901/*! @brief The value of the unknown at the time step has been calculated.
902 *
903 * This method advances the present until the time passed as parameter.
904 * It also updates the properties of the medium.
905 *
906 * @param (double temps) the time step for update
907 */
909{
910 // We turn the unknown wheel
911 // Update the unknown:
912 inconnue().mettre_a_jour(temps);
914
915 les_sources.mettre_a_jour(temps);
916
917 // We turn the boundary condition wheel
918 // Update the boundary condition:
919 if (temps > schema_temps().temps_courant()) domaine_Cl_dis().avancer(temps);
920}
921
922//update of champ_conserve / champ_convecte : called by Probleme_base::mettre_a_jour() after updating the medium
924{
925 if (reset && champ_conserve_) champ_conserve_->reset_champ_calcule(); //force calculation of all cells
926 if (reset && champ_convecte_) champ_convecte_->reset_champ_calcule();
927 if (champ_conserve_) champ_conserve_->mettre_a_jour(temps);
928 if (champ_convecte_) champ_convecte_->mettre_a_jour(temps);
929}
930
931/*! @brief Reinitialize what must be.
932 *
933 * This method is called when a time step is abandoned,
934 * for example because the calculation has diverged.
935 * So you must clean up what could pollute the new resolution.
936 *
937 * @throws WrongContext
938 */
940{
941 for (int i=0; i<nombre_d_operateurs(); i++)
944 if (champ_conserve_) champ_conserve_->abortTimeStep();
945 if (champ_convecte_) champ_convecte_->abortTimeStep();
946}
947
948/*! @brief Reset current time of the equation. Used from ICoCo.
949 * See documentation of Problem_base::resetTime()
950 */
952{
953 if(solveur_masse) solveur_masse->resetTime(time);
954 les_sources.resetTime(time);
955 le_dom_Cl_dis->resetTime(time);
956 for (int i=0; i<nombre_d_operateurs(); i++)
957 operateur(i).l_op_base().resetTime(time);
958 inconnue().resetTime(time);
959 if (champ_conserve_) champ_conserve_->resetTime(time);
960 if (champ_convecte_) champ_convecte_->resetTime(time);
961}
962
963
964/*! @brief virtual method to correct the unknown during implicit iterations for example K-eps must remain positive
965 *
966 * mass fractions between 0 and 1
967 *
968 */
972
973/*! @brief Everything that does not depend on other possible problems.
974 *
975 * @return (int) always returns 1
976 */
978{
979 solveur_masse->preparer_calcul();
980 int nb_op=nombre_d_operateurs();
981 for(int i=0; i<nb_op; i++)
982 {
984 dt_op_bak.add(0.);
985 }
987 double temps=schema_temps().temps_courant();
989 inconnue().changer_temps(temps);
991 le_dom_Cl_dis->initialiser(temps);
992
993 Nom msg=que_suis_je();
994 msg+=" dans Equation_base::preparer_calcul 1";
996
997 les_sources.initialiser(temps);
1000
1001 /* initialization of parametre_equation() by the time scheme if it allows it */
1002 if (sub_type(Schema_Implicite_base, schema_temps()))
1003 ref_cast(Schema_Implicite_base, schema_temps()).solveur()->get_and_set_parametre_equation(*this);
1004
1005 msg=que_suis_je();
1006 msg+=" dans Equation_base::preparer_calcul ";
1007 Debog::verifier(msg ,inconnue());
1008
1009 return 1;
1010}
1011
1012
1013/*! @brief Allocation and initialization of the unknown and boundary conditions until present+dt.
1014 *
1015 * + other initializations for calculations on the next
1016 * time step : operators, mass solver->
1017 *
1018 * @return (0 in case of error, 1 otherwise)
1019 */
1021{
1023 double temps_present=sch.temps_courant();
1024
1025 // The time step must be that of the scheme !!
1026 assert(sch.pas_de_temps()==dt);
1027
1028 // For each future time
1029 for (int i=1; i<=sch.nb_valeurs_futures(); i++)
1030 {
1031 double tps=sch.temps_futur(i);
1032 // Update of the time in the unknown
1034 if (champ_conserve_) champ_conserve_->changer_temps_futur(tps,i);
1035 if (champ_convecte_) champ_convecte_->changer_temps_futur(tps,i);
1037
1038 inconnue().futur(i)=inconnue().valeurs();
1039 if (champ_conserve_) champ_conserve_->futur(i) = champ_conserve().valeurs();
1040 if (champ_convecte_) champ_convecte_->futur(i) = champ_convecte().valeurs();
1042
1043 // Update of the time in the boundary conditions
1045 }
1046
1047 // Update of the default time of the boundary conditions
1049
1050 // Update of the operators
1051 for(int i=0; i<nombre_d_operateurs(); i++)
1052 operateur(i).mettre_a_jour(temps_present);
1053
1054 // Update of the mass solver at the present time
1055 if (solveur_masse)
1056 solveur_masse->mettre_a_jour(temps_present);
1057
1058 return true;
1059}
1060
1062{
1064 double temps_present=sch.temps_courant();
1065 double temps_futur=temps_present+sch.pas_de_temps();
1066
1067 // For each future time
1068 for (int i=1; i<=sch.nb_valeurs_futures(); i++)
1069 {
1070 double tps=sch.temps_futur(i);
1071 // Calculation of boundary conditions at this time.
1073 }
1074 // Calculation of the rate of change of boundary conditions between present and future times.
1075 domaine_Cl_dis().calculer_derivee_en_temps(temps_present,temps_futur);
1076
1077 //Update of operators
1078 for (int i = 0; i < nombre_d_operateurs(); i++)
1079 if (operateur(i).op_non_nul())
1080 operateur(i).l_op_base().mettre_a_jour(temps_present);
1081
1082 // Update of source terms at the present time
1083 les_sources.mettre_a_jour(temps_present);
1084
1085 return true;
1086}
1087
1088/*! @brief Returns the discretization associated with the equation.
1089 *
1090 * @return (Discretisation_base&) a discretization associated with the equation
1091 * @throws no problem associated
1092 */
1094{
1095 if(!mon_probleme)
1096 {
1097 Cerr << "Error : " << que_suis_je() << " has not been associated to a problem ! " << finl;
1098 exit();
1099 }
1100 return mon_probleme->discretisation();
1101}
1102
1104{
1105 const Equation_base& me_const = (*this); // to get a const equation !!!!!
1106 const Nom& nom_inco = me_const.inconnue().le_nom();
1107 Nom inco(nom_inco);
1108 inco += "_residu";
1109 if (motlu == Motcle(inco))
1110 if (!field_residu_)
1111 {
1112 discretisation().residu(domaine_dis(), inconnue(), field_residu_);
1113 champs_compris_.ajoute_champ(field_residu_);
1114 }
1115
1116 for (int i = 0; i < nombre_d_operateurs(); i++)
1117 if (operateur(i).op_non_nul())
1118 operateur(i).l_op_base().creer_champ(motlu);
1119
1120 for (auto &itr : les_sources)
1121 if (itr)
1122 itr->creer_champ(motlu);
1123}
1124
1125bool Equation_base::has_champ(const Motcle& nom, OBS_PTR(Champ_base) &ref_champ) const
1126{
1127 Nom inco_residu(inconnue().le_nom());
1128 inco_residu += "_residu";
1129
1130 if (nom == Motcle(inco_residu))
1131 {
1132 ref_champ = Equation_base::get_champ(nom);
1133 return true;
1134 }
1135
1136 for (const auto &itr : list_champ_combi)
1137 if (itr->le_nom() == nom)
1138 {
1139 ref_champ = Equation_base::get_champ(nom);
1140 return true;
1141 }
1142
1143 if (champs_compris_.has_champ(nom, ref_champ))
1144 return true;
1145
1146 if (milieu().has_champ(nom, ref_champ))
1147 return true;
1148
1149 for (int i = 0; i < nombre_d_operateurs(); i++)
1150 if (operateur(i).op_non_nul())
1151 if (operateur(i).l_op_base().has_champ(nom, ref_champ))
1152 return true;
1153
1154 for (const auto &itr : les_sources)
1155 if (itr)
1156 if (itr->has_champ(nom, ref_champ))
1157 return true;
1158
1159 return false; /* nothing found */
1160}
1161
1162bool Equation_base::has_champ(const Motcle& nom) const
1163{
1164 Nom inco_residu(inconnue().le_nom());
1165 inco_residu += "_residu";
1166
1167 if (nom == Motcle(inco_residu))
1168 return true;
1169
1170 for (const auto &itr : list_champ_combi)
1171 if (itr->le_nom() == nom)
1172 return true;
1173
1174 if (champs_compris_.has_champ(nom))
1175 return true;
1176
1177 if (milieu().has_champ(nom))
1178 return true;
1179
1180 for (int i = 0; i < nombre_d_operateurs(); i++)
1181 if (operateur(i).op_non_nul())
1182 if (operateur(i).l_op_base().has_champ(nom))
1183 return true;
1184
1185 for (const auto &itr : les_sources)
1186 if (itr)
1187 if (itr->has_champ(nom))
1188 return true;
1189
1190 return false; /* nothing found */
1191}
1192
1194{
1195 Nom inco_residu(inconnue().le_nom());
1196 inco_residu += "_residu";
1197 if (nom == Motcle(inco_residu))
1198 {
1199 Champ_Fonc_base& ch = ref_cast_non_const(Champ_Fonc_base, field_residu_.valeur());
1200 double temps_init = schema_temps().temps_init();
1201 if (((ch.temps() != inconnue().temps()) || (ch.temps() == temps_init)) && (inconnue().mon_equation_non_nul()))
1202 ch.mettre_a_jour(inconnue().temps());
1203 }
1204
1205 for (const auto &itr : list_champ_combi)
1206 if (itr->le_nom() == nom && itr->temps() != inconnue().temps())
1207 ref_cast_non_const(Champ_Fonc_base,itr.valeur()).mettre_a_jour(inconnue().temps());
1208
1209 OBS_PTR(Champ_base) ref_champ;
1210
1211 if (champs_compris_.has_champ(nom, ref_champ))
1212 return ref_champ;
1213
1214 if (milieu().has_champ(nom, ref_champ))
1215 return ref_champ;
1216
1217 for (int i = 0; i < nombre_d_operateurs(); i++)
1218 if (operateur(i).op_non_nul())
1219 if (operateur(i).l_op_base().has_champ(nom, ref_champ))
1220 return ref_champ;
1221
1222 for (const auto &itr : les_sources)
1223 if (itr)
1224 if (itr->has_champ(nom, ref_champ))
1225 return ref_champ;
1226
1227 throw std::runtime_error(std::string("Field ") + nom.getString() + std::string(" not found !"));
1228}
1229
1231{
1232 if (opt == DESCRIPTION)
1233 Cerr << que_suis_je() << " : " << champs_compris_.liste_noms_compris() << finl;
1234 else
1235 noms.add(champs_compris_.liste_noms_compris());
1236
1238 for (int i = 0; i < nombre_d_operateurs(); i++)
1239 if (operateur(i).op_non_nul())
1241
1242 for (const auto &itr : les_sources)
1243 if (itr)
1244 itr->get_noms_champs_postraitables(noms, opt);
1245}
1246
1247/*! @brief Calculation of the next time step.
1248 *
1249 * Returns the inverse of the sum of the inverses of the
1250 * time steps calculated by the operators.
1251 * These time steps are those for the explicit Euler scheme.
1252 * The time step is not limited by dt_max, this is done in corriger_dt_calcule
1253 *
1254 * @return (double) inverse of the sum of the inverses of the time steps calculated by the operators
1255 */
1257{
1258 double dt = 0;
1259 int nb_op = nombre_d_operateurs();
1260 const Schema_Temps_base& sch = le_schema_en_temps.valeur();
1261 bool cfl_based = sub_type(Schema_Euler_Implicite, sch) && ref_cast(Schema_Euler_Implicite, sch).facsec_cfl();
1262 for(int i=0; i<nb_op; i++)
1263 {
1264 const Operateur_base& op=operateur(i).l_op_base();
1265 bool diff_impl = sub_type(Operateur_Diff_base,op) && (sch.diffusion_implicite() || cfl_based);
1266 double dt_op = op.get_decal_temps()==1 ? DMAXFLOAT : operateur(i).calculer_pas_de_temps();
1267 Debog::verifier("Equation_base::calculer_pas_de_temps dt ",dt);
1268 if (dt_op>0 && !diff_impl)
1269 {
1270 // A half harmonic mean is justified by the fact that diffusion and convection are two phenomena that accumulate
1271 // The information at each time step cannot cross more than one computational cell so dt*U + dt*vitesse_diffusion(~alpha/dx) < dx
1272 // so dt < dx/(U+alpha/dx) = 1/(1/(dx/U)+1/(dx^2/alpha)) : it is indeed a half harmonic mean...
1273 dt = dt + 1./dt_op;
1274 }
1275 if (sch.limpr())
1276 {
1277 if (i == 0) Cout << " " << finl << "Printing of the next provisional time steps for the equation: " << que_suis_je() << finl;
1278 if (sub_type(Operateur_Conv_base,op))
1279 Cout << " convective";
1280 else if (sub_type(Operateur_Diff_base,op))
1281 Cout << " diffusive";
1282 else
1283 Cout << " operator ";
1284 Cout<<" time step : "<< dt_op_bak[i] << finl;
1285 }
1286 dt_op_bak[i]=dt_op;
1287 }
1288 if (dt==0.)
1289 dt = DMAXFLOAT;
1290 else
1291 dt = 1./dt;
1292
1293 // Implicit case
1294 if (sub_type(Schema_Euler_Implicite,sch) && nb_op>1)
1295 {
1296 if (cfl_based)
1297 // Case of zero initial velocity for example (dt=DMAFLOAT), we use the diffusion time step to start the calculation:
1298 if (dt>1e10) dt = dt_op_bak[0];
1299 }
1300 // Case of implicit diffusion and several operators:
1301 if (sub_type(Schema_Euler_explicite,sch) && nb_op>1)
1302 {
1303 if (sch.diffusion_implicite())
1304 {
1305 // Case of zero initial velocity for example (dt=DMAFLOAT), we use the diffusion time step to start the calculation:
1306 if (dt>1e10) dt = dt_op_bak[0];
1307 }
1308 else
1309 {
1310 // Warning on the first time steps if impliciting the diffusion
1311 // in an explicit scheme is worth it (dt_diff<<min(dt_max,dt_conv))
1312 // We do the test after the first 10 time steps in case of startup with zero velocity
1313 int nw = 100;
1314 if (sch.nb_pas_dt() > 10 && sch.nb_pas_dt() < nw && sch.limpr())
1315 if (dt_op_bak[0] < 0.01 * std::min(sch.pas_temps_max(), dt_op_bak[1]))
1316 {
1317 Cerr << finl << "**** Advice (printed only on the first " << nw << " time steps) ****" << finl;
1318 Cerr << "You could use diffusion_implicite option into the Euler scheme to increase" << finl;
1319 Cerr << "the stability time step of the " << this->que_suis_je() << " equation by impliciting the diffusive operator." << finl;
1320 }
1321 }
1322 }
1323 Debog::verifier("Equation_base::calculer_pas_de_temps dt ",dt);
1324 return dt;
1325}
1326
1327// Calculation of local time: Vect of size number of faces of the domain
1328// Calculate whether the "steady" option is used in the "Euler implicit"
1329// The local time step is a convection temp step
1331{
1332 int nb_op=nombre_d_operateurs();
1333 for(int i=0; i<nb_op; i++)
1334 {
1335 const Operateur_base& op=operateur(i).l_op_base();
1336 if (sub_type(Operateur_Conv_base,op))
1337 {
1338 if(sub_type(Op_Conv_negligeable, op))
1339 {
1340 Cerr<<"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"<<finl;
1341 Cerr<<"STEADY option is not compatible with the 'CONVECTION { NEGLIGEABLE }' model!"<<finl;
1342 Cerr << "Please, contact TRUST support." << finl;
1343 Cerr<<"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"<<finl;
1344 exit();
1345 }
1346 else
1348
1349 }
1350 }
1351
1352}
1353
1354/*! @brief Verifies the compatibility of boundary conditions with the equation.
1355 *
1356 * see Conds_lim::compatible_avec_eqn().
1357 *
1358 * @return (int)
1359 */
1361{
1362 const Conds_lim& les_cl=le_dom_Cl_dis->les_conditions_limites();
1363 les_cl.compatible_avec_eqn(*this);
1364 return les_cl.compatible_avec_discr(discretisation());
1365}
1366
1367/*! @brief Returns "indeterminate" Navier_Stokes_standard for example overrides this method
1368 *
1369 * to return "Hydraulic"
1370 *
1371 * @return (Motcle&) the name of the application domain
1372 */
1374{
1375 static Motcle defaut="indetermine";
1376 return defaut;
1377}
1378
1379/*! @brief Verification of the number of components read for the specification of a field.
1380 *
1381 * Currently used for reading an initial or boundary condition.
1382 *
1383 * @param (ch_ref : an unknown field of the considered equation)
1384 */
1385void Equation_base::verifie_ch_init_nb_comp(const Champ_Inc_base& ch_ref, const int nb_comp) const
1386{
1387 const Nature_du_champ nature = ch_ref.nature_du_champ();
1388 const int nb_composantes = ch_ref.nb_comp();
1389 const Nom& nom = ch_ref.le_nom();
1390
1391 if (nature==scalaire)
1392 {
1393 if (nb_comp!=1)
1394 {
1395 Cerr<<"The nature of the field "<<nom<<" unknown to the equation name "<<le_nom()<<" is scalar."<<finl;
1396 Cerr<<"The number of components readed for this field "<<nb_comp<<" is not compatible with its nature."<<finl;
1397 Cerr<<"We must read only one component for this field."<<finl;
1398 exit();
1399 }
1400 }
1401 else if (nature==quadrature_scalaire)
1402 {
1403 //DG size depends on the geometry
1404 }
1405 else if (nature==vectoriel)
1406 {
1407 if (nb_comp!=Objet_U::dimension)
1408 {
1409 Cerr<<"The nature of the field "<<nom<<" unknown to the equation name "<<le_nom()<<" is vector."<<finl;
1410 Cerr<<"The number of components readed for this field "<<nb_comp<<" is not compatible with its nature."<<finl;
1411 Cerr<<"It should read "<<Objet_U::dimension<<" components for this field."<<finl;
1412 exit();
1413 }
1414 }
1415 else if (nature==quadrature_vectoriel)
1416 {
1417 //DG size depends on the geometry
1418 }
1419 else if (nature==multi_scalaire)
1420 {
1421 if (nb_comp!=nb_composantes)
1422 {
1423 Cerr<<"The nature of the field "<<nom<<" unknown to the equation name "<<le_nom()<<" is multiscalar."<<finl;
1424 Cerr<<"The number of components readed for this field "<<nb_comp<<" does not match the number expected."<<finl;
1425 Cerr<<"It should read "<<nb_composantes<<" components for this field."<<finl;
1426 exit();
1427 }
1428 }
1429 else if (nature==basis_function_order_1_scalar)
1430 {
1431 /* if (nb_comp!=3)
1432 {
1433 Cerr<<"The nature of the field "<<nom<<" unknown to the equation name "<<le_nom()<<" is a basis_function_order_1_scalar."<<finl;
1434 Cerr<<"The number of components readed for this field "<<nb_comp<<" does not match the number expected."<<finl;
1435 Cerr<<"It should read "<<3<<" components for this field."<<finl;
1436 exit();
1437 } */
1438 }
1439 else if (nature==basis_function_order_2_scalar)
1440 {
1441 /* if (nb_comp!=6)
1442 {
1443 Cerr<<"The nature of the field "<<nom<<" unknown to the equation name "<<le_nom()<<" is a basis_function_order_2_scalar."<<finl;
1444 Cerr<<"The number of components readed for this field "<<nb_comp<<" does not match the number expected."<<finl;
1445 Cerr<<"It should read "<<6<<" components for this field."<<finl;
1446 exit();
1447 } */
1448 }
1449 else if (nature==basis_function_order_1_vectorial)
1450 {
1451 /* if (nb_comp!=3*Objet_U::dimension)
1452 {
1453 Cerr<<"The nature of the field "<<nom<<" unknown to the equation name "<<le_nom()<<" is a basis_function_order_1_vectorial."<<finl;
1454 Cerr<<"The number of components readed for this field "<<nb_comp<<" does not match the number expected."<<finl;
1455 Cerr<<"It should read "<<3*Objet_U::dimension<<" components for this field."<<finl;
1456 exit();
1457 } */
1458 }
1459 else if (nature==basis_function_order_2_vectorial)
1460 {
1461 /* if (nb_comp!=6*Objet_U::dimension)
1462 {
1463 Cerr<<"The nature of the field "<<nom<<" unknown to the equation name "<<le_nom()<<" is a basis_function_order_2_vectorial."<<finl;
1464 Cerr<<"The number of components readed for this field "<<nb_comp<<" does not match the number expected."<<finl;
1465 Cerr<<"It should read "<<6*Objet_U::dimension<<" components for this field."<<finl;
1466 exit();
1467 } */
1468 }
1469 else
1470 {
1471 Cerr<<"The nature of the field is not recognized"<<finl;
1472 exit();
1473 }
1474}
1475
1476/*! @brief Add convection term In: solution is the unknown I
1477 *
1478 * Out: secmem is increased by convection(I)
1479 *
1480 */
1481DoubleTab& Equation_base::derivee_en_temps_conv(DoubleTab& secmem, const DoubleTab& solution)
1482{
1483 double dt = le_schema_en_temps->pas_de_temps();
1484 double dt_convection;
1485 if (le_schema_en_temps->no_conv_subiteration_diffusion_implicite())
1486 {
1487 // false but allows not to sub-iterate on the convection
1488 dt_convection=1e38;
1489 }
1490 else
1491 dt_convection= operateur(1).calculer_pas_de_temps();
1492 if(inf_strict(dt_convection,dt))
1493 {
1494 // Several steps (nstep) are done if the convective time step
1495 // is smaller than the time step (dt) :
1496 double epsilon = 1.e-10;
1497 int nstep = (int)((dt / dt_convection)*(1+epsilon)) + 1 ;
1498 if (( nstep % 2 ) != 0 ) nstep += 1 ;
1499 double dt_loc = dt/double(nstep) ;
1500 Cout << "Convection Semi Implicite: Number of Sub-Cycles : " << nstep << " with dt_loc = " << dt_loc << " dt =" << dt << finl ;
1501
1502 secmem = 0;
1503 DoubleTrav solution_loc(solution);
1504 solution_loc = solution;
1505 DoubleTrav derivee;
1506 derivee.copy(secmem, RESIZE_OPTIONS::NOCOPY_NOINIT);
1507 for (int i=0; i<nstep; i++)
1508 {
1509 derivee = 0. ;
1510 operateur(1).ajouter(solution_loc, derivee); // derivee=conv(solution_loc)
1511 derivee.echange_espace_virtuel();
1512 secmem += derivee ;
1513
1514 // If it is not the last iteration:
1515 if (i+1 < nstep)
1516 {
1517 solveur_masse->appliquer(derivee);
1518 solution_loc.ajoute(dt_loc, derivee, VECT_REAL_ITEMS);
1519 solution_loc.echange_espace_virtuel();
1520 }
1521 }
1522 secmem /= double(nstep) ;
1523 return secmem;
1524 }
1525 else
1526 {
1527 operateur(1).ajouter(solution, secmem); // secmem+=conv(solution)
1528 return secmem;
1529 }
1530}
1531
1532/*! @brief Solve: (1/dt + M-1*L)*dI = M-1 * secmem with a Conjugate Gradient matrix-free algorithm by default
1533 *
1534 * L is the diffusion
1535 * M is the mass
1536 * In : solution=I(n)
1537 * Out: solution=dI/dt
1538 *
1539 */
1540void Equation_base::Gradient_conjugue_diff_impl(DoubleTrav& secmem, DoubleTab& solution, int size_terme_mul, const DoubleTab& terme_mul)
1541{
1542 if (le_schema_en_temps->impr_diffusion_implicite())
1543 Cout << "Implicited diffusion algorithm applied on " << que_suis_je() << " equation:" << finl;
1544 int marq_tot = 0;
1545 int size_s = sources().size();
1546 ArrOfInt marq(size_s);
1547 for (int i = 0; i < size_s; i++)
1548 {
1549 if (sub_type(Source_dep_inco_base, sources()(i).valeur()))
1550 {
1551 marq[i] = 1;
1552 marq_tot = 1;
1553 }
1554 }
1555 bool has_diffusion_implicit_solver = parametre_equation() &&
1556 sub_type(Parametre_diffusion_implicite, parametre_equation().valeur()) &&
1557 ref_cast(Parametre_diffusion_implicite, parametre_equation().valeur()).solveur();
1558 if (has_diffusion_implicit_solver)
1559 {
1560 // PL: Solve (M/dt + L)*dI = Secmem(sans diffusion) with a matrix build to use more solvers
1561 if (marq_tot)
1562 {
1563 Cerr << "solveur_dffusion_implicite can't be used yet with source terms depending from the unknowns. " << finl;
1564 Process::exit();
1565 }
1566 if (size_terme_mul)
1567 {
1568 Cerr << "solveur_dffusion_implicite can't be used yet with penalization. " << finl;
1569 Process::exit();
1570 }
1571 statistics().begin_count(STD_COUNTERS::matrix_assembly,statistics().get_last_opened_counter_level()+1);
1572 // We multiply secmem by M (which was divided by M before the call...)
1573 {
1574 // Scope to release Trav quickly
1575 DoubleTrav copie(secmem);
1576 copie = secmem;
1577 secmem = 0;
1578 solveur_masse->ajouter_masse(1, secmem, copie);
1579 }
1580 // Build matrix A:
1581 Matrice_Morse& matrice = ref_cast(Parametre_diffusion_implicite, parametre_equation().valeur()).matrice();
1582 if (matrice.ordre()==0) dimensionner_matrice(matrice);
1583 matrice.clean(); // A=0
1584 // Add diffusion matrix L into matrix
1585 {
1586 DoubleTrav present(solution); // I(n)
1587 present = solution;
1588 operateur(0).l_op_base().contribuer_a_avec(present, matrice); // A=L
1589 statistics().end_count(STD_COUNTERS::matrix_assembly);
1590 operateur(0).ajouter(present, secmem);
1591 statistics().begin_count(STD_COUNTERS::matrix_assembly,statistics().get_last_opened_counter_level()+1);
1592 matrice.ajouter_multvect(present, secmem);
1593 // Add M/dt into matrix
1594 schema_temps().ajouter_inertie(matrice,secmem,(*this)); // A=M/dt+L
1595 modifier_pour_Cl(matrice,secmem);
1596 statistics().end_count(STD_COUNTERS::matrix_assembly);
1597 // Solve to get I(n+1):
1598 SolveurSys& solveur = ref_cast(Parametre_diffusion_implicite, parametre_equation().valeur()).solveur();
1599 solveur->reinit(); // New matrix but same sparsity
1600 int niter = solveur.resoudre_systeme(matrice, secmem, solution);
1601 Cout << "Diffusion operator implicited for the equation " << que_suis_je()
1602 << " : Conjugate gradient converged in " << niter << " iterations." << finl;
1603 // CHD 230501 : Call to diffusive operator to update flux_bords (boundary fluxes):
1604 operateur(0).ajouter(inconnue(), secmem);
1605
1606 solution-=present; // dI=I(n+1)-I(n)
1607 solution/=schema_temps().pas_de_temps(); // dI/dt
1608 }
1609 }
1610 else
1611 {
1612 statistics().begin_count(STD_COUNTERS::implicit_diffusion,statistics().get_last_opened_counter_level()+1);
1613 int n = secmem.size_totale();
1614 if (solution.size_totale() != n)
1615 {
1616 Cerr << "the size of the solution and the second member does not match";
1617 exit();
1618 }
1619
1620 // The maximum number of iterations can now be limited by niter_max_diff_impl
1621 int nmax = le_schema_en_temps->niter_max_diffusion_implicite();
1622
1623 double aCKN = 1; // Crank - Nicholson -> 0.5
1624
1625 // Calculation of the Diagonal matrix
1626
1627 int precond_diag = 0;
1628 // We precondition by the diagonal if we penalize
1629 // because otherwise the initial residual is too large
1630 if (size_terme_mul) precond_diag = 1;
1631
1632 double seuil_diffusion_implicite = le_schema_en_temps->seuil_diffusion_implicite();
1633 // Possible recovery of Parametre_diffusion_implicite options
1634 if (parametre_equation() && (sub_type(Parametre_diffusion_implicite, parametre_equation().valeur())))
1635 {
1637 parametre_equation().valeur());
1638 if (param.crank())
1639 aCKN = 0.5;
1640 precond_diag = param.precoditionnement_diag();
1641 if (param.seuil_diffusion_implicite() > 0)
1642 seuil_diffusion_implicite = param.seuil_diffusion_implicite();
1643 if (param.nb_it_max() > 0)
1644 nmax = param.nb_it_max();
1645 }
1646 statistics().end_count(STD_COUNTERS::implicit_diffusion,0,0);
1647 /////////////////////
1648 // Preconditioning
1649 /////////////////////
1650 double dt = le_schema_en_temps->pas_de_temps();
1651 if (precond_diag)
1652 {
1653 statistics().begin_count(STD_COUNTERS::matrix_assembly,statistics().get_last_opened_counter_level()+1);
1654 const int nb_case = inconnue().valeurs().dimension_tot(0);
1655 const int nb_comp = inconnue().valeurs().line_size();
1656 if (nb_comp * nb_case != n)
1657 {
1658 Cerr << "the size of the unknown and the second member does not match" << finl;
1659 Cerr << "dimension_tot nbdim1 nb_comp = " << nb_case << " " << nb_comp << finl;
1660 Cerr << "size_totale = " << n << finl;
1661 exit();
1662 }
1663 if (diag_.ordre()==1) diag_.dimensionne_diag(n);
1664 diag_.clean(); // Set to 0
1666 for (int i = 0; i < size_s; i++)
1667 if (marq[i])
1668 sources()(i)->contribuer_a_avec(inconnue().valeurs(), diag_);
1669 // The diagonal is proportional to the control volume....
1670 // We must apply the mass solver
1671 DoubleTrav tab_tempo(inconnue().valeurs());
1672 {
1673 Matrice_Morse_View matrice; // ToDo Kokkos CMatrice_Morse_View diag = diag_.view_ro();
1674 matrice.set(diag_);
1675 DoubleTabView tempo = tab_tempo.view_wo();
1676 Kokkos::parallel_for(start_gpu_timer(__KERNEL_NAME__),
1677 Kokkos::RangePolicy<>(0, nb_case), KOKKOS_LAMBDA(
1678 const int ca)
1679 {
1680 for (int ncp = 0; ncp < nb_comp; ncp++)
1681 tempo(ca, ncp) = matrice.diag(ca * nb_comp + ncp);
1682 });
1683 end_gpu_timer(__KERNEL_NAME__);
1684 }
1685 solveur_masse->appliquer(tab_tempo);
1686 tab_tempo.echange_espace_virtuel();
1687 {
1688 // We invert... // Crank - Nicholson
1689 // The matrix corresponds to - the jacobian (to have a plus precisely, GF)
1690 CDoubleTabView tempo = tab_tempo.view_ro();
1691 CDoubleTabView terme_mul_v = terme_mul.view_ro();
1692 Matrice_Morse_View matrice; // ToDo Kokkos Matrice_Morse_View diag = diag_.view_rw();
1693 matrice.set(diag_);
1694 Kokkos::parallel_for(start_gpu_timer(__KERNEL_NAME__),
1695 Kokkos::RangePolicy<>(0, nb_case), KOKKOS_LAMBDA(
1696 const int ca)
1697 {
1698 double tmp = (size_terme_mul ? terme_mul_v(ca, 0) : 1) / dt;
1699 for (int ncpa = 0; ncpa < nb_comp; ncpa++)
1700 matrice.diag(ca * nb_comp + ncpa) = 1. / (tmp + tempo(ca, ncpa) * aCKN);
1701 });
1702 end_gpu_timer(__KERNEL_NAME__);
1703 }
1704 statistics().end_count(STD_COUNTERS::matrix_assembly);
1705 }
1706 // Lambda function to apply diffusive operator and source terms to avoid code repetition
1707 auto matvec = [&](const DoubleTab& input_field, DoubleTab& output_field)
1708 {
1709 // Stop the counter because operator diffusion is also counted
1710 statistics().end_count(STD_COUNTERS::implicit_diffusion,0,0);
1711 operateur(0).ajouter(input_field, output_field);
1712 if (marq_tot)
1713 {
1714 for (int i = 0; i < size_s; i++)
1715 if (marq[i])
1716 ref_cast(Source_dep_inco_base, sources()(i).valeur()).ajouter_(input_field, output_field);
1717 }
1718 statistics().begin_count(STD_COUNTERS::implicit_diffusion,statistics().get_last_opened_counter_level()+1);
1719 output_field*=-1;
1720 };
1721 statistics().begin_count(STD_COUNTERS::implicit_diffusion,statistics().get_last_opened_counter_level()+1);
1722 // We use p to calculate phiB :
1723 DoubleTrav p(solution);
1724 DoubleTrav moins_phiB(solution); // the Boundary part of the operator with p=0
1725 matvec(p, moins_phiB);
1726
1727 DoubleTrav resu(solution);
1728 matvec(solution, resu);
1729 solveur_masse->appliquer(resu);
1731 {
1732 // Create scope to release DoubleTrav quickly
1733 DoubleTrav sol;
1734 if (size_terme_mul)
1735 {
1736 sol = solution;
1737 ToDo_Kokkos("critical for IBC");
1738 //operator_multiply(sol, term_mul);
1739 // tab_multiply_any_shape(sol, terme_mul);
1740 for (int i = 0; i < size_terme_mul; i++)
1741 sol(i) *= terme_mul(i);
1742 }
1743 else
1744 sol.ref(solution);
1745
1746 // we remove the source terms depending on the unknown ; we will add them back later
1747 if (marq_tot)
1748 {
1749 DoubleTrav sum_sources(secmem);
1750 statistics().end_count(STD_COUNTERS::implicit_diffusion,0,0);
1751 for (int i = 0; i < size_s; i++)
1752 if (marq[i])
1753 sources()(i).ajouter(sum_sources);
1754 statistics().begin_count(STD_COUNTERS::implicit_diffusion,statistics().get_last_opened_counter_level()+1);
1755 solv_masse().appliquer(sum_sources);
1756 secmem.ajoute(-1., sum_sources); // ,VECT_REAL_ITEMS);
1757 }
1758 secmem.ajoute(1. / dt, sol, VECT_REAL_ITEMS);
1759 resu.ajoute(1. / dt, sol, VECT_REAL_ITEMS);
1760 }
1761
1762 DoubleTrav residu; // residu = Ax
1763 residu = resu;
1764 residu -= secmem; // residu = Ax-B
1765 DoubleTrav z;
1766 if (precond_diag)
1767 {
1768 z = residu;
1769 diag_.multvect(residu, z); // z = D-1(Ax-B)
1770 }
1771 else
1772 z.ref(residu); // z = Ax-B
1773 p -= z;
1774
1775 double initial_residual = mp_carre_norme_vect(z); // =||Ax(0)-B|| ou ||D-1.(Ax(0)-B)||
1776 /* Alternative computation of initial_residual:
1777 DoubleTab tmp(secmem);
1778 if (precond_diag)
1779 diag_.multvect(secmem, tmp);
1780 else
1781 tmp.ref(secmem);
1782 double initial_residual = mp_carre_norme_vect(tmp) ; // =||B|| ou ||D-1.B|| */
1783
1784 if (le_schema_en_temps->impr_diffusion_implicite()) Cout << "Residu(0)=" << initial_residual << finl;
1785 double seuil = seuil_diffusion_implicite * seuil_diffusion_implicite;
1786 // We calculate a relative threshold (PL: 18/07/12, I have the impression there's an error, it's seuil*=initial_residual;
1787 // Moreover, it would be good to do residual = sqrt(mp_carre_norme_vect(tmp)) at several places...
1788 if (initial_residual > seuil)
1789 seuil = seuil_diffusion_implicite * initial_residual;
1790
1791 double residual = seuil;
1792 double prodrz_old = mp_prodscal(residu, z);
1793 DoubleTrav pp;
1794 if (size_terme_mul) pp=p;
1795 DoubleTrav merk;
1796 merk = solution;
1797 int niter = 0;
1798 if (initial_residual != 0)
1799 while (niter++ <= nmax)
1800 {
1801 resu = moins_phiB; // We remove the contribution of the boundaries.
1803 matvec(p, resu);
1804 solveur_masse->appliquer(resu);
1805
1806 if (aCKN!=1) resu *= aCKN; // Crank - Nicholson
1807 if (size_terme_mul)
1808 {
1809 for (int i = 0; i < size_terme_mul; i++)
1810 pp(i) = p(i) * terme_mul(i);
1811 resu.ajoute(1. / dt, pp, VECT_REAL_ITEMS);
1812 }
1813 else
1814 resu.ajoute(1. / dt, p, VECT_REAL_ITEMS);
1815 double alfa = prodrz_old / mp_prodscal(resu, p);
1816 // Pointless to do a virtual space exchange:
1817 solution.ajoute(alfa, p, VECT_REAL_ITEMS);
1818 residu.ajoute(alfa, resu, VECT_REAL_ITEMS);
1819
1820 if (precond_diag)
1821 diag_.multvect(residu, z); // preconditioning by diag^(-1)
1822 // otherwise z=residu
1823
1824 // Optimization: combine 2 MPI reductions into 1
1825 residual = local_carre_norme_vect(z);
1826 double prodrz_new = local_prodscal(residu, z);
1827 Process::mp_sum_for_each(residual, prodrz_new);
1828 if (le_schema_en_temps->impr_diffusion_implicite())
1829 Cout << "Iteration n=" << niter << " Residu(n)/Residu(0)=" << residual / initial_residual << finl;
1830
1831 if (residual<seuil)
1832 break;
1833 else
1834 {
1835 p *= (prodrz_new / prodrz_old);
1836 p -= z;
1837 prodrz_old = prodrz_new;
1838 // We warn if convergence is abnormally long on very large cases
1839 if (niter == 100)
1840 {
1841 Cout
1842 << "Already 100 iterations of the conjugate gradient solver used for diffusion implicited algorithm."
1843 << finl;
1844 Cout
1845 << "The calculation is diverging, may be because of incorrect initial and/or boundary conditions for the equation "
1846 << que_suis_je() << finl;
1847 Cout
1848 << "You may also try to stop the calculation and rerun it with a lower facsec, say 0.9 or may be less: 0.5."
1849 << finl;
1850 }
1851 }
1852 }
1853 if ((le_schema_en_temps->no_error_if_not_converged_diffusion_implicite() == 0) && (niter > nmax))
1854 {
1855 Cerr << "No convergence of the implicited diffusion algorithm for the equation " << que_suis_je() << " in "
1856 << nmax << " iterations." << finl;
1857 Cerr << "Residue : " << residual << " Threshold : " << seuil << finl;
1858 Cerr << "======================================================================================" << finl;
1859 Cerr << "The problem is post processed to help you to see where the non convergence is located." << finl;
1861 probleme().postraiter(1);
1862 probleme().mettre_a_jour(schema_temps().temps_courant());
1863 probleme().finir();
1864 Cerr << "The problem " << probleme().le_nom() << " has been saved too." << finl;
1865 exit();
1866 }
1867 else
1868 Cout << "Diffusion operator implicited for the equation " << que_suis_je()
1869 << " : Conjugate gradient converged in " << niter << " iterations." << finl;
1870 solution -= merk;
1871 solution.echange_espace_virtuel();
1872
1873 // CHD 230501 : last call to update flux_bords (boundary fluxes):
1874 resu = 0.;
1875 matvec(inconnue().valeurs(), resu);
1876
1877 // Since 1.6.8 returns dI/dt:
1878 solution/=dt;
1879
1880 // End the counter
1881 statistics().end_count(STD_COUNTERS::implicit_diffusion);
1882 }
1883}
1884
1886{
1887 return d;
1888}
1890{
1891 return d;
1892}
1893
1894const RefObjU& Equation_base::get_modele(Type_modele type) const
1895{
1896 return liste_modeles_(0);
1897
1898}
1899// MODIF ELI LAUCOIN (22/11/2007) : I add an advance and a backward
1900// by default, this only advances or goes back the unknown (and champ_conserve_ if initialized)
1902{
1903 inconnue().avancer(i);
1904 if (champ_conserve_) champ_conserve_->avancer(i);
1905 if (champ_convecte_) champ_convecte_->avancer(i);
1906}
1907
1909{
1910 inconnue().reculer(i);
1911 if (champ_conserve_) champ_conserve_->reculer(i);
1912 if (champ_convecte_) champ_convecte_->reculer(i);
1913}
1914// END MODIF ELI LAUCOIN (22/11/2007)
1915
1916
1917#define BLOQUE Cerr<<__PRETTY_FUNCTION__<< " "<<__FILE__<<":"<<(int)__LINE__<<" not coded, retrieves coding simpler" <<finl;exit()
1918
1919// methods for the implicit
1920
1921/* can use a memoization (discretisations PolyMAC_HFV)*/
1923{
1924 if (matrice_init)
1925 {
1926 matrice.get_set_tab1().ref_array(matrice_stockee.get_set_tab1());
1927 matrice.get_set_tab2().ref_array(matrice_stockee.get_set_tab2());
1928 matrice.get_set_coeff().ref_array(matrice_stockee.get_set_coeff());
1929 matrice.set_nb_columns(matrice_stockee.nb_colonnes());
1930 matrice.sorted_ = matrice_stockee.sorted_;
1931 matrice.constant_stencil() = matrice_stockee.constant_stencil();
1932 matrice.get_set_coeff() = 0.0;
1933 return;
1934 }
1935
1936 dimensionner_matrice_sans_mem(matrice); //le vrai appel
1937
1938 matrice.get_set_coeff() = 0.0; // just to be sure ...
1939
1940 if (probleme().discretisation().is_poly_family() || probleme().discretisation().is_vef())
1941 {
1942 // PL: ToDo why sorting leads to issue in VEF ???
1943 if (probleme().discretisation().is_poly_family())
1944 {
1945 matrice.sort_stencil();
1946 matrice_stockee.sorted_ = 1;
1947 }
1948 matrice.constant_stencil()=true; // stencil will not change anymore
1949 matrice_stockee.get_set_tab1().ref_array(matrice.get_set_tab1());
1950 matrice_stockee.get_set_tab2().ref_array(matrice.get_set_tab2());
1951 matrice_stockee.get_set_coeff().ref_array(matrice.get_set_coeff());
1952 matrice_stockee.set_nb_columns(matrice.nb_colonnes());
1953 matrice_stockee.constant_stencil()=matrice.constant_stencil();
1954 matrice_init = 1;
1955 }
1956}
1957
1959{
1960 // [ABN]: dimensioning of the implicit matrix: it can receive input from:
1961 // - the operators
1962 // - the mass solver
1963 // - the sources
1964 // We proceed in this order, with the idea that operators should take most of the slots in the Morse
1965 // structure. Having the mass solver first would lead (in VDF, EF and VEF) to diagonal slots
1966 // being reserved before sub-diagonal terms. In the Morse structure coefficients would then
1967 // not be ordered by increasing columns number. This is not desirable, notably for EF which seem to
1968 // be sensitive to this ordering (although they should not ....). Some Baltiks too (FLICA5 - thermic part)
1969 // rely on this.
1970 if (matrice_init) // memoization
1971 {
1972 matrice.get_set_tab1().ref_array(matrice_stockee.get_set_tab1());
1973 matrice.get_set_tab2().ref_array(matrice_stockee.get_set_tab2());
1974 matrice.get_set_coeff().ref_array(matrice_stockee.get_set_coeff());
1975 matrice.set_nb_columns(matrice_stockee.nb_colonnes());
1976 matrice.constant_stencil() = matrice_stockee.constant_stencil();
1977 return;
1978 }
1979
1980 bool isInit = false;
1981 // Operators first (they're likely to take most of the slots in the Morse structure)
1982 for(int opp=0; opp < nombre_d_operateurs(); opp++)
1983 {
1984 if (!isInit)
1985 operateur(opp).l_op_base().dimensionner(matrice);
1986 else
1987 {
1988 Matrice_Morse mat2;
1989 operateur(opp).l_op_base().dimensionner(mat2);
1990 if (mat2.nb_colonnes())
1991 matrice += mat2; // this only works if the matrix has been given its overall size first
1992 }
1993 isInit = isInit || (matrice.nb_colonnes() != 0);
1994
1995 }
1996
1997 // ... then the mass solver ...
1998 if (!isInit)
1999 solv_masse().dimensionner(matrice);
2000 else
2001 {
2002 Matrice_Morse mat2;
2003 solv_masse().dimensionner(mat2);
2004 matrice += mat2; // this only works if the matrix has been given its overall size first
2005 }
2006
2007 // and finally sources (mass solver has surely done something already so no need for "isInit" test)
2008 les_sources.dimensionner(matrice);
2009}
2010
2011void Equation_base::dimensionner_termes_croises(Matrice_Morse& matrice, const Probleme_base& autre_pb, int nl, int nc)
2012{
2013 matrice.dimensionner(nl, nc, 0);
2014 for(int i = 0; i < nombre_d_operateurs(); i++)
2015 {
2016 Matrice_Morse mat2;
2017 operateur(i).l_op_base().dimensionner_termes_croises(mat2, autre_pb, nl, nc);
2018 if (mat2.nb_colonnes()) matrice += mat2;
2019 }
2020}
2021
2022void Equation_base::ajouter_termes_croises(const DoubleTab& inco, const Probleme_base& autre_pb, const DoubleTab& autre_inco, DoubleTab& resu) const
2023{
2024 for (int i = 0; i < nombre_d_operateurs(); i++)
2025 operateur(i).l_op_base().ajouter_termes_croises(inco, autre_pb, autre_inco, resu);
2026}
2027
2028void Equation_base::contribuer_termes_croises(const DoubleTab& inco, const Probleme_base& autre_pb, const DoubleTab& autre_inco, Matrice_Morse& matrice) const
2029{
2030 for (int i = 0; i < nombre_d_operateurs(); i++)
2031 operateur(i).l_op_base().contribuer_termes_croises(inco, autre_pb, autre_inco, matrice);
2032}
2033
2034// adds the contributions of operators and sources
2035void Equation_base::assembler(Matrice_Morse& matrice, const DoubleTab& inco, DoubleTab& resu)
2036{
2037 // Test to verify the contribuer_a_avec method
2038 for (int op=0; op<nombre_d_operateurs(); op++)
2039 operateur(op).l_op_base().tester_contribuer_a_avec(inco, matrice);
2040
2041 // Contribution of operators and sources:
2042 // [Vol/dt+A]Inco(n+1)=sum(residual)+Vol/dt*Inco(n)
2043 // Typically: if Op=flux(Inco) then the implicit matrix A contains a contribution -dflux/dInco
2044 // Example: Op convective flux in VDF:
2045 // Op=T*u*S and A=-d(T*u*S)/dT=-u*S
2048 {
2049 // We calculate sum(residual) by contribuer_au_second_membre (typically non-implicit boundary conditions)
2050 // This approach requires coding 3 methods (contribuer_a_avec, contribuer_au_second_membre and add for explicit)
2051 sources().contribuer_a_avec(inco,matrice);
2052 statistics().end_count(STD_COUNTERS::matrix_assembly,0,0);
2053 sources().ajouter(resu);
2054 statistics().begin_count(STD_COUNTERS::matrix_assembly,statistics().get_last_opened_counter_level()+1);
2055 matrice.ajouter_multvect(inco, resu); // Add source residual first
2056 for (int op = 0; op < nombre_d_operateurs(); op++)
2057 {
2058 operateur(op).l_op_base().contribuer_a_avec(inco, matrice);
2060 }
2061 }
2062 else if (type_codage==Discretisation_base::VIA_AJOUTER)
2063 {
2064 // We calculate sum(residual) by sum(operator)+sources+A*Inco(n)
2065 // This approach requires coding only two methods (contribuer_a_avec and add)
2066 // So a bit more costly in calculation time but less code to write/maintain
2067
2068 /*
2069 * XXX XXX XXX Elie Saikali, Gauthier Fauchet, aout 2025 :
2070 *
2071 * We need to calculate : [ I^(n+1) - I^n ] / dt = f( I^(n+1) )
2072 *
2073 * But we dont know now what is f( I^(n+1) )
2074 *
2075 * 2 possibilities
2076 *
2077 * 1- [ I^(k+1) - I^k ] / dt = f( I^(k) )
2078 * then use fixed point algo to have I^k = I^(k+1) = I^(n+1)
2079 *
2080 *
2081 * 2- Use Newton algorithm (we use this here)
2082 *
2083 * f( I^(n+1) ) = f ( I^n ) + df/dI ( I^(n+1) - I^n )
2084 *
2085 * df/dI is the matrix -A obtained by the method contribuer_a_avec
2086 *
2087 * so : f( I^(n+1) ) = f ( I^n ) - A ( I^(n+1) - I^n ) = f ( I^n ) - A * I^(n+1) + A * I^n
2088 *
2089 * Thus, 1/dt I^(n+1) + A * I^(n+1) = 1/dt I^n + f ( I^n ) + A * I^n
2090 *
2091 * I^(n+1) [ Identity / dt + A ] = 1/dt I^n + f ( I^n ) + A * I^n
2092 *
2093 * and f ( I^n ) is obtained by the method ajouter
2094 */
2095 for (int op=0; op<nombre_d_operateurs(); op++)
2096 {
2097 operateur(op).l_op_base().contribuer_a_avec(inco, matrice);
2098 statistics().end_count(STD_COUNTERS::matrix_assembly,0,0);
2099 operateur(op).ajouter(resu);
2100 statistics().begin_count(STD_COUNTERS::matrix_assembly,statistics().get_last_opened_counter_level()+1);
2101 }
2102 sources().contribuer_a_avec(inco,matrice);
2103 statistics().end_count(STD_COUNTERS::matrix_assembly,0,0);
2104 sources().ajouter(resu);
2105 statistics().begin_count(STD_COUNTERS::matrix_assembly,statistics().get_last_opened_counter_level()+1);
2106 matrice.ajouter_multvect(inco, resu); // Addition of A*Inco(n)
2107 // PL (11/04/2018): We would like to calculate the contribution of sources first
2108 // as in the VIA_CONTRIBUER_AU_SECOND_MEMBRE case but the Canal_perio_3D case (keps
2109 // periodic crashes: there is a periodicity error in the source terms of the KEps model...
2110 }
2111 else
2112 {
2113 Cerr << "Unknown value in Equation_base::assembler for " << (int)type_codage << finl;
2114 Process::exit();
2115 }
2116}
2117
2118// modifies the matrix and the second member according to the boundary conditions
2119void Equation_base::modifier_pour_Cl(Matrice_Morse& mat_morse, DoubleTab& secmem) const
2120{
2121 for (int op=0; op<nombre_d_operateurs(); op++)
2122 {
2123 operateur(op).l_op_base().modifier_pour_Cl(mat_morse,secmem);
2124 }
2125}
2126// assembles, adds inertia, and modifies for boundary conditions.
2127void Equation_base::assembler_avec_inertie( Matrice_Morse& mat_morse,const DoubleTab& present, DoubleTab& secmem)
2128{
2129 statistics().begin_count(STD_COUNTERS::matrix_assembly,statistics().get_last_opened_counter_level()+1);
2130 assembler(mat_morse,present,secmem);
2131 schema_temps().ajouter_inertie(mat_morse,secmem,(*this));
2132 modifier_pour_Cl(mat_morse,secmem);
2133 statistics().end_count(STD_COUNTERS::matrix_assembly);
2134}
2135
2136/* verifies that all terms are compatible */
2138{
2139 int ok = 1;
2140 /* operators, mass, sources */
2141 for (int op = 0; op < nombre_d_operateurs(); op++) ok &= operateur(op).l_op_base().has_interface_blocs();
2143 for (int i = 0; i < les_sources.size(); i++) ok &= les_sources(i)->has_interface_blocs();
2144 return ok;
2145}
2146
2147/* like dimensionner_matrice(), but without memoization */
2148void Equation_base::dimensionner_blocs(matrices_t matrices, const tabs_t& semi_impl) const
2149{
2150 /* operators, mass, sources */
2151 int nb = nombre_d_operateurs();
2152 for (int op = 0; op < nb; op++) operateur(op).l_op_base().dimensionner_blocs(matrices, semi_impl);
2153 solv_masse().dimensionner_blocs(matrices);
2154 for (int i = 0; i < les_sources.size(); i++) les_sources(i)->dimensionner_blocs(matrices, semi_impl);
2155}
2156
2157void Equation_base::assembler_blocs(matrices_t matrices, DoubleTab& secmem, const tabs_t& semi_impl) const
2158{
2159 /* setting to zero */
2160 secmem = 0;
2161 for (auto && i_m : matrices) i_m.second->get_set_coeff() = 0;
2162 /* operators, sources, mass */
2163 for (int i = 0; i < nombre_d_operateurs(); i++)
2164 operateur(i).l_op_base().ajouter_blocs(matrices, secmem, semi_impl);
2165
2166 statistics().end_count(STD_COUNTERS::ajouter_blocs,0,0);
2167
2168 statistics().begin_count(STD_COUNTERS::source_terms,statistics().get_last_opened_counter_level()+1);
2169 for (int i = 0; i < les_sources.size(); i++)
2170 les_sources(i)->ajouter_blocs(matrices, secmem, semi_impl);
2171
2172 statistics().end_count(STD_COUNTERS::source_terms);
2173
2174
2175 statistics().begin_count(STD_COUNTERS::ajouter_blocs,statistics().get_last_opened_counter_level()+1);
2176 if (!(discretisation().is_poly_family() || probleme().que_suis_je().debute_par("Pb_Multiphase") || que_suis_je().debute_par("Equation_flux")))
2177 {
2178 const std::string& nom_inco = inconnue().le_nom().getString();
2179 Matrice_Morse *mat = matrices.count(nom_inco) ? matrices.at(nom_inco) : nullptr;
2180 if(mat) mat->ajouter_multvect(inconnue().valeurs(), secmem);
2181 }
2182}
2183
2184void Equation_base::assembler_blocs_avec_inertie(matrices_t matrices, DoubleTab& secmem, const tabs_t& semi_impl)
2185{
2186 statistics().begin_count(STD_COUNTERS::ajouter_blocs,statistics().get_last_opened_counter_level()+1);
2187 assembler_blocs(matrices, secmem, semi_impl);
2189 schema_temps().ajouter_blocs(matrices, secmem, *this);
2190
2191 if (!discretisation().is_poly_family())
2192 {
2193 const std::string& nom_inco = inconnue().le_nom().getString();
2194 Matrice_Morse *mat = matrices.count(nom_inco) ? matrices.at(nom_inco) : nullptr;
2195 modifier_pour_Cl(*mat,secmem);
2196 }
2197 statistics().end_count(STD_COUNTERS::ajouter_blocs);
2198}
2199
2200/* creation of champ_conserve_, cl_champ_conserve_ */
2202{
2203 if (champ_conserve_) return; //already done
2204 int Nt = inconnue().nb_valeurs_temporelles(),
2205 Nl = inconnue().valeurs().size_reelle_ok() ? inconnue().valeurs().dimension(0) : -1,
2206 Nc = inconnue().valeurs().line_size();
2207 //champ_conserve_ : same type / support as the unknown
2208 discretisation().creer_champ(champ_conserve_, domaine_dis(), inconnue().que_suis_je(), "N/A", "N/A", Nc, Nl, Nt, schema_temps().temps_courant());
2209 champ_conserve_->associer_eqn(*this);
2210 auto nom_fonc = get_fonc_champ_conserve();
2211 champ_conserve_->nommer(nom_fonc.first);
2212 champ_conserve_->init_champ_calcule(*this, nom_fonc.second);
2213}
2214
2215/* default calculation method of champ_conserve : product of coefficient_temporel * unknown */
2216void Equation_base::calculer_champ_conserve(const Objet_U& obj, DoubleTab& val, DoubleTab& bval, tabs_t& deriv)
2217{
2218 const Equation_base& eqn = ref_cast(Equation_base, obj);
2219 const Champ_base *coeff = eqn.solv_masse().has_coefficient_temporel() ? &eqn.get_champ(eqn.solv_masse().get_name_of_coefficient_temporel()) : nullptr; //temporal coeff
2220 const Champ_Inc_base& inco = eqn.inconnue();
2221 ConstDoubleTab_parts part(inco.valeurs());
2222 //value of the field itself
2223 val = part[0];
2224 if (coeff) tab_multiply_any_shape(val, coeff->valeurs(), VECT_ALL_ITEMS);
2225
2226 bval = inco.valeur_aux_bords();
2227 if (coeff) tab_multiply_any_shape(bval, coeff->valeur_aux_bords(), VECT_ALL_ITEMS);
2228
2229 DoubleTab& der = deriv[inco.le_nom().getString()];
2230 if (coeff) der = coeff->valeurs();
2231 else der.resize(val.dimension_tot(0), val.line_size()), der = 1;
2232}
2233
2234// Printing of the residual in file (usually dt_ev)
2235// This method can be overridden by equations
2236// printing particular residuals (K-Eps, Concentrations,...)
2238{
2239 int size = residu_.size_array();
2240 for (int i=0; i<size; i++)
2241 {
2242 Nom tab = schema_temps().norm_residu() == "max" ? "\t " : "\t\t ";
2243 fic << residu_(i) << tab;
2244 int nb_unknowns = inconnue().valeurs().dimension_tot(0);
2245 double residual_limit = ( nb_unknowns == 0 ? DMAXFLOAT : DMAXFLOAT/nb_unknowns);
2246 if (residu_(i)>residual_limit)
2247 {
2248 fic << finl;
2249 Cerr << "Equation " << le_nom() << " is diverging:" << finl;
2250 Cerr << "A residual ||dI/dt|| for this equation is bigger than " << residual_limit << " !" << finl;
2251 Cerr << "Have a look at the " << nom_du_cas() << ".dt_ev file." << finl;
2252 Process::exit();
2253 }
2254 }
2255 // Display min/max
2256 if (schema_temps().impr_extremums() && limpr())
2257 {
2258 double vmax = mp_max_vect(inconnue().valeurs());
2259 double vmin = mp_min_vect(inconnue().valeurs());
2260 Cout << finl << inconnue().le_nom() << " field [min/max]: ";
2261 if (je_suis_maitre()) printf("[ %.2e / %.2e ]",vmin,vmax);
2262 Cout << finl;
2263 }
2264}
2265
2266// Returns the expression of the residual (likewise can be overridden)
2268{
2269 int size = residu_.size_array();
2270 Nom tmp(""),ajout("");
2271 if (probleme().is_coupled())
2272 {
2273 ajout="_";
2274 ajout+=probleme().le_nom();
2275 }
2276 for (int i=0; i<size; i++)
2277 {
2278 Nom norm = schema_temps().norm_residu();
2279 tmp+="Ri=";
2280 tmp+=norm;
2281 if(norm != "max") tmp+="-norm";
2282 tmp+=ajout;
2283 tmp+="|d";
2284 // How to name clearly AND concisely
2285 // the field of the expression:
2286 if (size==1)
2287 {
2288 // Scalar field
2289 tmp+=(Motcle)inconnue().le_nom()[0];
2290 }
2291 else
2292 {
2293 // Multi-scalar field
2294 if (inconnue().nom_compo(0)[0]!=inconnue().nom_compo(size-1)[0])
2295 {
2296 // Example, K_Eps field
2297 tmp+=(Motcle)inconnue().nom_compo(i)[0];
2298 }
2299 else
2300 {
2301 // Example, concentration field
2302 tmp+=(Motcle)inconnue().le_nom()[0];
2303 tmp+=(Nom)i;
2304 }
2305 }
2306 tmp+="/dt|\t ";
2307 }
2308 return tmp;
2309}
2310
2311// Dimension the residual arrays
2313{
2314 if (size==0)
2315 size = inconnue().valeurs().line_size();
2316 residu_.resize(size);
2317 residu_initial_.resize(size);
2318 residu_=0;
2319 residu_initial_=0;
2320}
2321
2322// Fills the field_residu_ field if it exists
2323void Equation_base::set_residuals(const DoubleTab& residual)
2324{
2325 if(field_residu_)
2326 {
2327 DoubleTab& tab = field_residu_->valeurs();
2328 if (tab.dimension_tot(0) == residual.dimension_tot(0))
2329 tab = residual;
2330 else
2331 {
2332 ConstDoubleTab_parts parts(residual);
2333 if (parts[0].dimension_tot(0) == tab.dimension_tot(0))
2334 tab = parts[0];
2335 }
2336 }
2337}
2338
2340{
2341 Cerr<<"The method operateur_fonctionnel is not coded for equation "<<que_suis_je()<<finl;
2342 exit();
2343 throw;
2344}
2345
2347{
2348 Cerr<<"The method operateur_fonctionnel is not coded for equation "<<que_suis_je()<<finl;
2349 exit();
2350 throw;
2351}
2352
class Champ_Don_base base class of Given Fields (not calculated)
class Champ_Fonc_base Base class of fields that are functions of a calculated quantity
void mettre_a_jour(double temps) override
Time update of the field.
Class Champ_Inc_base.
DoubleTab & futur(int i=1) override
Returns field values at instant t+i.
virtual void associer_domaine_cl_dis(const Domaine_Cl_dis_base &)
virtual int nb_valeurs_temporelles() const
Returns the number of temporal values currently kept.
void mettre_a_jour(double temps) override
Performs a time update of the unknown field.
void resetTime(double time) override
double changer_temps(const double temps) override
Sets the time of the field.
virtual std::vector< YAML_data > data_a_sauvegarder() const
for PDI IO: retrieve name, type and dimensions of the field to save/restore.
DoubleTab & valeurs() override
Returns the array of field values at the current time.
virtual void associer_eqn(const Equation_base &)
Associates the field with the equation of which it represents an unknown.
virtual void verifie_valeurs_cl()
int sauvegarder(Sortie &) const override
Saves the unknown field to an output stream.
Champ_Inc_base & avancer(int i=1)
Advances the current pointer by i time steps, in the list of kept temporal values.
Champ_Inc_base & reculer(int i=1)
Rewinds the current pointer by i time steps, in the list of kept temporal values.
int reprendre(Entree &) override
Reads an unknown field from an input stream for a restart.
DoubleTab valeur_aux_bords() const override
Returns the field value at the boundary faces.
double changer_temps_futur(double, int i=1)
Sets the time of the i-th future field.
class Champ_base This class is the base of the fields hierarchy.
Definition Champ_base.h:43
Champ_base & affecter(const Champ_base &)
Assign a field to another.
double temps() const
Returns the time of the field.
virtual void abortTimeStep()
class Cond_lim_base Base class for the hierarchy of classes that represent the different boundary con...
virtual Frontiere_dis_base & frontiere_dis()
Returns the discretized boundary to which the boundary conditions apply.
class Conds_lim This class represents a vector of boundary conditions.
Definition Conds_lim.h:32
int compatible_avec_eqn(const Equation_base &) const
Returns whether ALL boundary conditions in the vector are compatible with the equation passed as para...
Definition Conds_lim.h:84
int compatible_avec_discr(const Discretisation_base &) const
Returns whether ALL boundary conditions in the vector are compatible with the discretization passed a...
Definition Conds_lim.h:96
static void verifier(const char *const msg, double)
Definition Debog.cpp:21
class Discretisation_base This class represents a spatial discretization scheme, which
static void creer_champ(OWN_PTR(Champ_Inc_base)&ch, const Domaine_dis_base &z, const Nom &type, const Nom &nom, const Nom &unite, int nb_comp, int nb_ddl, int nb_pas_dt, double temps, const Nom &directive=NOM_VIDE, const Nom &nom_discretisation=NOM_VIDE)
Static method that creates an OWN_PTR(Champ_Inc_base) of the specified type.
virtual Nom get_name_of_type_for(const Nom &class_operateur, const Nom &type_operteur, const Equation_base &eqn, const OBS_PTR(Champ_base)&champ_supp=OBS_PTR(Champ_base)()) const
Fills the Nom type depending on the class of operator, the type of operator and the equation.
virtual type_calcul_du_residu codage_du_calcul_du_residu() const
virtual void residu(const Domaine_dis_base &, const Champ_Inc_base &, OWN_PTR(Champ_Fonc_base)&) const
void discretiser_champ(const Motcle &directive, const Domaine_dis_base &z, const Nom &nom, const Nom &unite, int nb_comp, int nb_pas_dt, double temps, OWN_PTR(Champ_Inc_base)&champ, const Nom &sous_type=NOM_VIDE) const
void calculer_derivee_en_temps(double t1, double t2)
Computes the growth rate of unsteady BCs between t1 and t2.
virtual void imposer_cond_lim(Champ_Inc_base &, double)=0
void set_temps_defaut(double temps)
Changes the i-th future time of all BCs.
virtual void mettre_a_jour(double temps)
Performs a time update of all boundary conditions.
int avancer(double temps)
Rotates the wheel of all BCs up to the given time.
void changer_temps_futur(double temps, int i)
Changes the i-th future time of all BCs.
const Nom & le_nom() const override
Returns the name of the Objet_U. Virtual method to override: returns "neant" in this implementation.
class Domaine_dis_base This class is the base of the hierarchy of discretized domains.
Class defining operators and methods for all reading operation in an input flow (file,...
Definition Entree.h:42
class Equation_base The role of an equation is the calculation of one or more fields....
virtual int equation_non_resolue() const
Matrice_Morse matrice_stockee
virtual void dimensionner_termes_croises(Matrice_Morse &matrice, const Probleme_base &autre_pb, int nl, int nc)
virtual void ajouter_termes_croises(const DoubleTab &inco, const Probleme_base &autre_pb, const DoubleTab &autre_inco, DoubleTab &resu) const
virtual void set_param(Param &titi) const override
const Nom & le_nom() const override
Returns the name of the equation.
virtual void associer_milieu_equation()
virtual Entree & lire_cond_init(Entree &)
Reading of initial conditions in an input stream.
int reprendre(Entree &) override
We resume the unknown from an input stream.
virtual const Milieu_base & milieu() const =0
DoubleTab & derivee_en_temps_conv(DoubleTab &, const DoubleTab &)
Add convection term In: solution is the unknown I.
virtual const RefObjU & get_modele(Type_modele type) const
virtual void avancer(int i=1)
virtual void associer_domaine_dis(const Domaine_dis_base &)
Associates the discretized domain with the equation.
void set_residuals(const DoubleTab &residual)
void nommer(const Nom &nom) override
Method called when the object instance is created in the data file (Interprete::ajouter).
virtual void dimensionner_matrice_sans_mem(Matrice_Morse &mat_morse)
virtual int verif_Cl() const
Verifies the compatibility of boundary conditions with the equation.
virtual std::vector< YAML_data > data_a_sauvegarder() const
for PDI IO: retrieve name, type and dimensions of the data to save/restore. This has to be overrode f...
virtual void imprimer(Sortie &os) const
Prints the equation operators if the time scheme indicates it is necessary.
virtual void associer_pb_base(const Probleme_base &)
Associates with the Problem passed as parameter.
Sources & sources()
Returns the source terms associated with the equation.
void set_calculate_time_derivative(int i)
OWN_PTR(Parametre_equation_base) &parametre_equation()
virtual void assembler_blocs_avec_inertie(matrices_t matrices, DoubleTab &secmem, const tabs_t &semi_impl={})
const Discretisation_base & discretisation() const
Returns the discretization associated with the equation.
void init_champ_conserve() const
Champ_Inc_base & champ_conserve() const
Solveur_Masse_base & solv_masse()
Returns the mass solver associated with the equation.
virtual void valider_iteration()
virtual method to correct the unknown during implicit iterations for example K-eps must remain positi...
virtual void mettre_a_jour_champs_conserves(double temps, int reset=0)
virtual const Champ_Inc_base & inconnue() const =0
Nom create_polymacfamily_syno(const Nom &field_tag) const
Create a synonym of a field name in order to ensure backward compatibility with old names of the Poly...
OBS_PTR(Schema_Temps_base) le_schema_en_temps
const Champ_base & get_champ(const Motcle &nom) const override
virtual Entree & lire_cl(Entree &)
Reading of boundary conditions in an input stream.
virtual DoubleTab & corriger_derivee_expl(DoubleTab &)
Matrice_Morse_Diag diag_
virtual void dimensionner_blocs(matrices_t matrices, const tabs_t &semi_impl={}) const
virtual void contribuer_termes_croises(const DoubleTab &inco, const Probleme_base &autre_pb, const DoubleTab &autre_inco, Matrice_Morse &matrice) const
virtual void mettre_a_jour(double temps)
The value of the unknown at the time step has been calculated.
virtual int impr(Sortie &os) const
Prints the equation operators to an output stream, unconditionally.
virtual void abortTimeStep()
Reinitialize what must be.
virtual void assembler_avec_inertie(Matrice_Morse &mat_morse, const DoubleTab &present, DoubleTab &secmem)
int calculate_time_derivative() const
virtual void completer()
Completes the construction (initialization) of objects associated with the equation.
virtual int nombre_d_operateurs() const =0
Champs_Fonc list_champ_combi
virtual double get_time_factor() const
Sources les_sources
void get_noms_champs_postraitables(Noms &nom, Option opt=NONE) const override
virtual int preparer_calcul()
Everything that does not depend on other possible problems.
virtual void imprime_residu(SFichier &)
virtual int nombre_d_operateurs_tot() const
virtual bool updateGivenFields()
bool has_champ(const Motcle &nom, OBS_PTR(Champ_base) &ref_champ) const override
DoubleList dt_op_bak
virtual std::pair< std::string, fonc_calc_t > get_fonc_champ_conserve() const
void initialise_residu(int=0)
virtual void assembler(Matrice_Morse &mat_morse, const DoubleTab &present, DoubleTab &secmem)
virtual void modifier_pour_Cl(Matrice_Morse &mat_morse, DoubleTab &secmem) const
virtual DoubleTab & derivee_en_temps_inco(DoubleTab &)
Returns the time derivative of the unknown I of the equation: dI/dt = M-1*(sum(operators(I) + sources...
int sauvegarder(Sortie &) const override
We save the unknown, then the source terms to an output stream.
virtual Domaine_Cl_dis_base & domaine_Cl_dis()
Returns the discretized boundary condition domain associated with the equation.
virtual const Champ_Inc_base & derivee_en_temps() const
Probleme_base & probleme()
Returns the problem associated with the equation.
virtual void verifie_ch_init_nb_comp(const Champ_Inc_base &ch_ref, const int nb_comp) const
Verification of the number of components read for the specification of a field.
int lire_motcle_non_standard(const Motcle &, Entree &) override
Reads non-simple-type parameters of an Objet_U from an input stream.
virtual void associer_sch_tps_base(const Schema_Temps_base &)
Associates the time scheme with the equation.
virtual void reculer(int i=1)
virtual DoubleTab & corriger_derivee_impl(DoubleTab &)
int limpr() const
Ask the time scheme if an output is needed.
void creer_champ(const Motcle &motlu) override
Schema_Temps_base & schema_temps()
Returns the time scheme associated with the equation.
void Gradient_conjugue_diff_impl(DoubleTrav &secmem, DoubleTab &solution)
virtual bool initTimeStep(double dt)
Allocation and initialization of the unknown and boundary conditions until present+dt.
virtual void dimensionner_matrice(Matrice_Morse &mat_morse)
virtual const Operateur & operateur_fonctionnel(int) const
virtual void discretiser()
Discretizes the equation.
void calculer_pas_de_temps_locaux(DoubleTab &) const
virtual Entree & lire_sources(Entree &)
Reading of source terms in an input stream.
virtual int has_interface_blocs() const
virtual void resetTime(double time)
Reset current time of the equation. Used from ICoCo. See documentation of Problem_base::resetTime().
Champs_compris champs_compris_
virtual void assembler_blocs(matrices_t matrices, DoubleTab &secmem, const tabs_t &semi_impl={}) const
virtual const Operateur & operateur(int) const =0
static void calculer_champ_conserve(const Objet_U &obj, DoubleTab &val, DoubleTab &bval, tabs_t &deriv)
virtual Champ_Inc_base & champ_convecte() const
Domaine_dis_base & domaine_dis()
Returns the discretized domain associated with the equation.
virtual const Motcle & domaine_application() const
Returns "indeterminate" Navier_Stokes_standard for example overrides this method.
virtual double calculer_pas_de_temps() const
Calculation of the next time step.
virtual Nom expression_residu()
const Nom & le_nom() const override
Returns the name of the field.
virtual int nb_comp() const
Definition Field_base.h:56
const Nom & nom_compo(int) const
Returns the name of the i-th component of the field.
virtual Nature_du_champ nature_du_champ() const
Definition Field_base.h:77
const Nom & le_nom() const override
Returns the name of the Objet_U. Virtual method to override: returns "neant" in this implementation.
Definition Frontiere.h:49
const Frontiere & frontiere() const
Returns the associated geometric boundary.
Matrice_Base class - Base class of the matrix hierarchy.
virtual DoubleVect & ajouter_multvect(const DoubleVect &x, DoubleVect &r) const
Matrix-vector multiply-accumulate operation (saxpy).
Matrice_Morse_Sym class - Represents a sparse symmetric matrix M stored in Morse format.
Matrice_Morse class - Represents a (sparse) matrix M, not necessarily square,.
void clean() override
Remplit la matrice avec des zeros.
auto & get_set_tab2()
int ordre() const override
Returns the order of the matrix: - the number of rows if the matrix is square.
bool & constant_stencil() const
void dimensionner(int n, _SIZE_ nnz)
Size the matrix with n lines and n columns and nnz zero-values coefficients.
void set_nb_columns(const int)
auto & get_set_coeff()
int nb_colonnes() const override
Return local number of columns (=size on the current proc).
auto & get_set_tab1()
void set_est_definie(int)
virtual void associer_equation(const Equation_base *eqn) const
void get_noms_champs_postraitables(Noms &nom, Option opt=NONE) const override
void associer_eqn(const Equation_base &)
Associates an equation with the object.
Definition MorEqn.cpp:28
A character string (Nom) in uppercase.
Definition Motcle.h:26
class Nom: a character string for naming TRUST objects.
Definition Nom.h:31
virtual int debute_par(const char *const n) const
Definition Nom.cpp:314
const std::string & getString() const
Definition Nom.h:92
const Nom & le_nom() const override
Returns *this.
Definition Nom.cpp:555
An array of character strings (VECT(Nom)).
Definition Noms.h:26
Base class for TRUST objects (Objet_U).
Definition Objet_U.h:68
friend class Entree
Definition Objet_U.h:71
static int dimension
Definition Objet_U.h:94
friend class Sortie
Definition Objet_U.h:70
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
static const Nom & nom_du_cas()
Returns a constant reference to the case name. This method is static.
Definition Objet_U.cpp:145
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
Classe Op_Conv_negligeable This class represents a negligible convection operator.
Operateur_Conv_base This class is the base of the hierarchy of operators representing.
Operateur_Diff_base This class is the base of the hierarchy of operators representing.
class Operateur_base This class is the base of the hierarchy of objects representing an
virtual void ajouter_termes_croises(const DoubleTab &inco, const Probleme_base &autre_pb, const DoubleTab &autre_inco, DoubleTab &resu) const
virtual void modifier_pour_Cl(Matrice_Morse &, DoubleTab &) const
DOES NOTHING - to override in derived classes.
const SolveurSys & get_solveur() const
void creer_champ(const Motcle &motlu) override
virtual void contribuer_a_avec(const DoubleTab &, Matrice_Morse &) const
DOES NOTHING - to override in derived classes.
SolveurSys & set_solveur()
virtual int has_interface_blocs() const
void tester_contribuer_a_avec(const DoubleTab &, const Matrice_Morse &)
virtual void mettre_a_jour(double temps)
DOES NOTHING - to override in derived classes.
Matrice & set_matrice()
int get_decal_temps() const
virtual void preparer_calcul()
virtual void dimensionner_termes_croises(Matrice_Morse &, const Probleme_base &autre_pb, int nl, int nc) const
virtual void dimensionner(Matrice_Morse &) const
DOES NOTHING - to override in derived classes.
const Matrice & get_matrice() const
void get_noms_champs_postraitables(Noms &nom, Option opt=NONE) const override
virtual void dimensionner_blocs(matrices_t matrices, const tabs_t &semi_impl={ }) const
virtual void contribuer_au_second_membre(DoubleTab &) const
DOES NOTHING - to override in derived classes.
virtual void resetTime(double time)
virtual void abortTimeStep()
virtual int systeme_invariant() const
virtual void ajouter_blocs(matrices_t matrices, DoubleTab &secmem, const tabs_t &semi_impl={ }) const
virtual void contribuer_termes_croises(const DoubleTab &inco, const Probleme_base &autre_pb, const DoubleTab &autre_inco, Matrice_Morse &matrice) const
class Operateur Generic class of the operator hierarchy.
Definition Operateur.h:39
virtual Operateur_base & l_op_base()=0
double calculer_pas_de_temps() const
Computes the next time step.
int impr(Sortie &os) const
Prints the operator to an output stream unconditionally.
virtual DoubleTab & calculer(const DoubleTab &, DoubleTab &) const =0
virtual void mettre_a_jour(double temps)
Performs a time update of the operator.
virtual DoubleTab & ajouter(const DoubleTab &, DoubleTab &) const =0
virtual void completer()
Updates the references of the objects associated with the operator.
void calculer_pas_de_temps_locaux(DoubleTab &) const
Calculate the next local time steps.
Helper class to factorize the readOn method of Objet_U classes.
Definition Param.h:112
void ajouter(const char *keyword, const int *value, Param::Nature nat=Param::OPTIONAL)
Register an integer parameter.
Definition Param.cpp:364
@ REQUIRED
Definition Param.h:115
void ajouter_non_std(const char *keyword, const Objet_U *value, Param::Nature nat=Param::OPTIONAL)
Register a keyword handled by Objet_U::lire_motcle_non_standard.
Definition Param.cpp:489
class Parametre_diffusion_implicite A Parametre_diffusion_implicite object groups together the variou...
class Parametre_implicite A Parametre_implicite object groups together the various
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
class Probleme_base It is a Probleme_U that is not a coupling.
int postraiter(int force=1) override
If force=1, performs post-processing regardless of the post-processing frequencies.
const Domaine & domaine() const
Returns the domain associated with the problem.
const Discretisation_base & discretisation() const
Returns the discretization associated with the problem.
virtual void mettre_a_jour(double temps)
Performs a time update of the problem.
virtual void finir()
Finalizes post-processing and saves the problem to a file.
static void mp_sum_for_each(T &arg1, T &arg2)
C++14 compatible mp_sum_for_each: combine multiple mp_sum calls into one collective operation Usage: ...
Definition Process.cpp:208
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
static int je_suis_maitre()
Returns 1 if on the master processor of the current group (i.e. me() == 0), 0 otherwise.
Definition Process.cpp:82
SFichier is to the C++ ofstream class what Sortie is to the C++ ostream class.
Definition SFichier.h:29
: class Schema_Euler_explicite This class represents an explicit Euler time scheme: U(n+1) = U(n) + d...
class Schema_Implicite_base Base class for all implicit time schemes.
class Schema_Temps_base
int diffusion_implicite() const
Returns 1 if the time scheme has been read with diffusion_implicite.
int limpr() const
Returns 1 if there is a need to perform a print (cf dt_impr) Returns 0 otherwise.
double temps_courant() const
Returns the current time.
const Nom & norm_residu() const
virtual void ajouter_inertie(Matrice_Base &mat_morse, DoubleTab &secmem, const Equation_base &eqn) const
virtual double temps_futur(int i) const =0
double pas_temps_max() const
Returns the maximum time step.
virtual void ajouter_blocs(matrices_t matrices, DoubleTab &secmem, const Equation_base &eqn, const tabs_t &semi_impl={}) const
double pas_de_temps() const
Returns the current time step (delta_t).
virtual int nb_valeurs_futures() const =0
virtual void modifier_second_membre(const Equation_base &eqn, DoubleTab &secmem)
int nb_pas_dt() const
Returns the number of time steps performed.
double temps_init() const
Returns the initial time.
virtual double temps_defaut() const =0
virtual void completer()=0
class SolveurSys A SolveurSys represents any class
Definition SolveurSys.h:32
int resoudre_systeme(const Matrice_Base &matrice, const DoubleVect &secmem, DoubleVect &solution)
void set_penalisation_flag(int pen)
virtual void dimensionner(Matrice_Morse &matrix) const
const Nom & get_name_of_coefficient_temporel() const
int has_coefficient_temporel() const
virtual void dimensionner_blocs(matrices_t matrices, const tabs_t &semi_impl={}) const
virtual Matrice_Base & ajouter_masse(double dt, Matrice_Base &matrice, int penalisation=1) const
virtual DoubleTab & corriger_solution(DoubleTab &x, const DoubleTab &y, int incr=0) const
virtual DoubleTab & appliquer(DoubleTab &) const
Returns appliquer_impl(x/temporal_coefficient) if a temporal coefficient is set, otherwise returns ap...
virtual int has_interface_blocs() const
Base class for output streams.
Definition Sortie.h:52
class Source_dep_inco_base Sources inheriting from this class must implement ajouter_ in a way that a...
class Sources Sources represents a list of Source objects.
Definition Sources.h:31
void contribuer_a_avec(const DoubleTab &, Matrice_Morse &) const
Contribution to the implicit matrix of source terms. By default, no contribution.
Definition Sources.cpp:201
DoubleTab & ajouter(DoubleTab &) const
Adds the contribution of all sources in the list to the array passed as parameter,...
Definition Sources.cpp:85
_SIZE_ size_array() const
virtual void ref(const TRUSTTab &)
Definition TRUSTTab.tpp:308
std::enable_if_t< is_default_exec_space< EXEC_SPACE >, View< _TYPE_, _SHAPE_ > > view_wo()
Definition TRUSTTab.h:276
void resize(_SIZE_ n, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTTab.tpp:469
_SIZE_ dimension_tot(int) const override
Definition TRUSTTab.tpp:160
void copy(const TRUSTTab &, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
Definition TRUSTTab.tpp:622
std::enable_if_t< is_default_exec_space< EXEC_SPACE >, ConstView< _TYPE_, _SHAPE_ > > view_ro() const
Definition TRUSTTab.h:261
_SIZE_ dimension(int d) const
Definition TRUSTTab.tpp:133
_SIZE_ size_totale() const
Definition TRUSTVect.tpp:61
int line_size() const
Definition TRUSTVect.tpp:67
_SIZE_ size_reelle_ok() const
Definition TRUSTVect.tpp:38
void ajoute(_SCALAR_TYPE_ alpha, const TRUSTVect &y, Mp_vect_options opt=VECT_ALL_ITEMS)
Definition TRUSTVect.tpp:52
virtual void echange_espace_virtuel(IsExchangeBlocking exchange_type=IsExchangeBlocking::DefaultBlocking, const std::string kernel_name="noname")
static int is_PDI_restart()