14#include "arcane/utils/StringBuilder.h"
15#include "arcane/utils/Iostream.h"
16#include "arcane/utils/PlatformUtils.h"
17#include "arcane/utils/Convert.h"
18#include "arcane/utils/NotImplementedException.h"
19#include "arcane/utils/ArgumentException.h"
20#include "arcane/utils/FloatingPointExceptionSentry.h"
22#include "arcane/core/ISubDomain.h"
23#include "arcane/core/IParallelMng.h"
25#include "arcane/core/IMesh.h"
26#include "arcane/core/IMeshSubMeshTransition.h"
27#include "arcane/core/ItemGroup.h"
28#include "arcane/core/Service.h"
29#include "arcane/core/Timer.h"
30#include "arcane/core/FactoryService.h"
31#include "arcane/core/ItemPrinter.h"
32#include "arcane/core/IItemFamily.h"
33#include "arcane/core/MeshVariable.h"
34#include "arcane/core/VariableBuildInfo.h"
36#include "arcane/std/MeshPartitionerBase.h"
37#include "arcane/std/MetisMeshPartitioner_axl.h"
39#include "arcane_internal_config.h"
42#define MPICH_SKIP_MPICXX
43#define OMPI_SKIP_MPICXX
47#if (PARMETIS_MAJOR_VERSION < 4)
48#error "Your version of Parmetis is too old .Version 4.0+ is required"
51typedef real_t realtype;
54#include "arcane/std/PartitionConverter.h"
55#include "arcane/std/GraphDistributor.h"
56#include "arcane/std/internal/MetisWrapper.h"
66using MetisCallStrategy = TypesMetisMeshPartitioner::MetisCallStrategy;
67using MetisEmptyPartitionStrategy = TypesMetisMeshPartitioner::MetisEmptyPartitionStrategy;
74class MetisMeshPartitioner
96 bool m_disable_floatingexception =
false;
107 const String& Name)
const;
113MetisMeshPartitioner::
114MetisMeshPartitioner(
const ServiceBuildInfo& sbi)
115: ArcaneMetisMeshPartitionerObject(sbi)
117 m_parallel_mng = mesh()->parallelMng();
119 if (s==
"1" || s==
"TRUE")
120 m_disable_floatingexception =
true;
129 Int32 nb_part = m_parallel_mng->commSize();
142 if (m_disable_floatingexception){
161 bool force_partition =
false;
168 info() <<
"INFO: Unable to test load balancing on a single sub-domain";
176 bool force_full_repartition =
false;
178 force_full_repartition |= (m_nb_refine < 0);
180 if (nb_part != nb_rank) {
181 force_full_repartition =
true;
182 force_partition =
true;
185 info() <<
"WARNING: compensating the potential lack of 'Metis' options in case of manual instanciation";
186 Integer max_diffusion_count = 10;
187 Real imbalance_relative_tolerance = 4;
188 float tolerance_target = 1.05f;
189 bool dump_graph =
false;
190 MetisCallStrategy call_strategy = MetisCallStrategy::one_processor_per_node;
191 bool in_out_digest =
false;
196 if (call_strategy_env ==
"all-processors"){
197 call_strategy = MetisCallStrategy::all_processors;
198 }
else if (call_strategy_env ==
"one-processor-per-node") {
199 call_strategy = MetisCallStrategy::one_processor_per_node;
200 }
else if (call_strategy_env ==
"two-processors-two-nodes") {
201 call_strategy = MetisCallStrategy::two_processors_two_nodes;
202 }
else if (call_strategy_env ==
"two-gathered-processors") {
203 call_strategy = MetisCallStrategy::two_gathered_processors;
204 }
else if (call_strategy_env ==
"two-scattered-processors") {
205 call_strategy = MetisCallStrategy::two_scattered_processors;
206 }
else if (!call_strategy_env.
null()) {
207 ARCANE_FATAL(
"Invalid value '{0}' for ARCANE_METIS_CALL_STRATEGY environment variable",call_strategy_env);
211 in_out_digest = (v.value()!=0);
213 dump_graph = (v.value()!=0);
216 max_diffusion_count =
options()->maxDiffusiveCount();
217 imbalance_relative_tolerance =
options()->imbalanceRelativeTolerance();
218 tolerance_target = (float)(
options()->toleranceTarget());
219 dump_graph =
options()->dumpGraph();
220 call_strategy =
options()->metisCallStrategy();
221 in_out_digest =
options()->inputOutputDigest();
224 if (max_diffusion_count > 0)
225 force_full_repartition |= (m_nb_refine>=max_diffusion_count);
227 force_full_repartition |= (
imbalance()>1.0);
230 initConstraints(
false);
236 if (is_shared_memory)
237 call_strategy = MetisCallStrategy::one_processor_per_node;
239 idxtype nb_weight = nbCellWeight();
241 info() <<
"Load balancing with Metis nb_weight=" << nb_weight <<
" initial=" << initial_partition
242 <<
" call_strategy=" << (int)call_strategy
243 <<
" is_shared_memory?=" << is_shared_memory
244 <<
" disabling_fpe?=" << m_disable_floatingexception
245 <<
" sizeof(idxtype)==" <<
sizeof(idxtype);
248 initial_partition =
true;
259 Integer nb_own_cell = nbOwnCellsWithConstraints();
261 Int32 nb_empty_part = 0;
264 metis_vtkdist[0] = 0;
265 for(
Integer i=0; i<nb_rank; ++i ){
267 Int32 n = global_nb_own_cell[i];
271 metis_vtkdist[i+1] =
static_cast<idxtype
>(total_nb_cell);
276 info() <<
"Total nb_cell=" << total_nb_cell <<
" nb_empty_partition=" << nb_empty_part;
277 if (total_nb_cell==0){
278 info() <<
"INFO: mesh '" <<
mesh->name() <<
" has no cell. No partitioning is needed";
296 Integer nb_max_face_neighbour_cell = 0;
303 if (cellUsedWithConstraints(item)){
304 cell_metis_uid[item] = mid;
307 if (item.
hasFlags(ItemFlags::II_HasEdgeFor1DItems))
308 nb_max_face_neighbour_cell += item.
nbEdge();
310 nb_max_face_neighbour_cell += item.
nbFace();
312 cell_metis_uid.synchronize();
315 _initUidRef(cell_metis_uid);
318 cell_metis_uid.setUsed(
false);
321 metis_xadj.
reserve(nb_own_cell+1);
324 metis_adjncy.
reserve(nb_max_face_neighbour_cell);
335 if (!cellUsedWithConstraints(item))
338 metis_xadj.
add(metis_adjncy.
size());
340 getNeighbourCellsUidWithConstraints(item, neighbour_cells, &edge_weights);
342 metis_adjncy.
add(
static_cast<idxtype
>(neighbour_cells[z]));
344 metis_xadj.
add(metis_adjncy.
size());
348 idxtype numflags = 0;
349 idxtype nparts =
static_cast<int>(nb_part);
356 if (!s.
null() && (s.
upper() ==
"PARTICLES"))
362 bool cond = (nb_weight == 1);
375 cells_weights = cellsWeightsWithConstraints(CheckedConvert::toInteger(nb_weight));
377 metis_ubvec.
resize(CheckedConvert::toInteger(nb_weight));
378 metis_ubvec.
fill(tolerance_target);
380 converter.reset(CheckedConvert::toInteger(nb_weight));
383 cond = converter.isBalancable<realtype>(cells_weights, metis_ubvec, nb_part);
385 info() <<
"We have to increase imbalance :";
386 info() << metis_ubvec;
387 for (
auto& tol: metis_ubvec ) {
397 const bool do_print_weight =
false;
398 if (do_print_weight){
400 Integer iteration =
mesh->subDomain()->commonVariables().globalIteration();
407 for (
int i = 0; i < metis_xadj.
size()-1 ; ++i) {
408 dumpy <<
" Weight uid=" << i;
409 for(
int j=0 ; j < nb_weight ; ++j ){
410 dumpy <<
" w=" << *(metis_vwgt.
begin()+i*nb_weight+j)
411 <<
"(" << cells_weights[i*nb_weight+j] <<
")";
419 converter.computeContrib(edge_weights);
426 if (!initial_partition) {
427 cells_size = cellsSizeWithConstraints();
428 converter.computeContrib(cells_size, (
Real)itr);
433 idxtype metis_options[4];
434 metis_options[0] = 0;
442 metis_options[0] = 1;
443 metis_options[1] = 0;
444 metis_options[2] = m_random_seed;
445 metis_options[3] = 1;
451 idxtype metis_edgecut = 0;
456 std::chrono::high_resolution_clock clock;
457 auto start_time = clock.now();
469 bool redistribute =
true;
470 bool redistribute_by_wrapper =
false;
472 if (call_strategy == MetisCallStrategy::all_processors || call_strategy == MetisCallStrategy::two_scattered_processors) {
473 redistribute =
false;
476 if (call_strategy == MetisCallStrategy::two_processors_two_nodes || call_strategy == MetisCallStrategy::two_scattered_processors) {
477 redistribute_by_wrapper =
true;
494 bool gd_allow_only_one_rank =
false;
495 if (is_shared_memory || (nb_empty_part!=0 && initial_partition))
496 gd_allow_only_one_rank =
true;
501 if ((nb_empty_part+1)==nb_rank){
502 info() <<
"Initialize GraphDistributor with max rank=1";
503 gd.initWithMaxRank(1);
505 else if (call_strategy == MetisCallStrategy::two_gathered_processors && (nb_rank > 2)) {
507 info() <<
"Initialize GraphDistributor with max rank=2";
508 gd.initWithMaxRank(2);
511 info() <<
"Initialize GraphDistributor with one rank per node"
512 <<
" (allow_one?=" << gd_allow_only_one_rank <<
")";
518 info() <<
"Using GraphDistributor?=" << redistribute;
520 metis_xadj = gd.convert<idxtype>(metis_xadj, &metis_part,
true);
521 metis_vwgt = gd.convert<idxtype>(metis_vwgt);
522 metis_adjncy = gd.convert<idxtype>(metis_adjncy);
523 metis_ewgt = gd.convert<idxtype>(metis_ewgt);
524 if (!initial_partition)
525 metis_vsize = gd.convert<idxtype>(metis_vsize);
526 metis_pm = gd.subParallelMng();
528 metis_vtkdist.
resize(gd.size()+1);
529 if (gd.contribute()) {
531 Integer nbVertices = metis_part.size();
533 metis_vtkdist[0] = 0;
534 for (
int i = 1 ; i < metis_vtkdist.
size() ; ++i) {
535 metis_vtkdist[i] = metis_vtkdist[i-1] + buffer[i-1];
538 metis_options[3] = PARMETIS_PSR_UNCOUPLED ;
544 MPI_Comm metis_mpicomm =
static_cast<MPI_Comm
>(metis_pm->
communicator());
546 bool do_call_metis =
true;
548 do_call_metis = gd.contribute();
555 _writeGraph(metis_pm, metis_vtkdist, metis_xadj, metis_adjncy, metis_vwgt, metis_ewgt, name.
toString());
559 const Integer tpwgt_size = CheckedConvert::toInteger(nb_part) * CheckedConvert::toInteger(nb_weight);
561 float fill_value = (float)(1.0/(
double)nparts);
562 tpwgt.
fill(fill_value);
564 int retval = METIS_OK;
568 force_partition |= initial_partition;
569 force_full_repartition |= force_partition;
570 if (force_full_repartition){
572 if (force_partition) {
573 info() <<
"Metis: use a complete partitioning.";
575 redistribute_by_wrapper,
576 metis_vtkdist.
data(),
581 &wgtflag,&numflags,&nb_weight,
582 &nparts, tpwgt.
data(),
584 metis_options,&metis_edgecut,
588 info() <<
"Metis: use a complete REpartitioning.";
590 redistribute_by_wrapper,
591 metis_vtkdist.
data(),
597 &wgtflag,&numflags,&nb_weight,
598 &nparts,tpwgt.
data(),
600 &itr, metis_options,&metis_edgecut,
608 info() <<
"Metis: use a diffusive REpartitioning";
609 retval = ParMETIS_V3_RefineKway(metis_vtkdist.
data(),
614 &wgtflag,&numflags,&nb_weight,
615 &nparts,tpwgt.
data(),
617 metis_options,&metis_edgecut,
621 if (retval != METIS_OK) {
622 ARCANE_FATAL(
"Error while computing ParMetis partitioning r='{0}'",retval);
626 metis_part = gd.convertBack<idxtype>(metis_part, nb_own_cell);
629 auto end_time = clock.now();
630 std::chrono::microseconds duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
631 info() <<
"Time to partition using parmetis = " << duration.count() <<
" us ";
637 switch(
options()->emptyPartitionStrategy()){
638 case MetisEmptyPartitionStrategy::DoNothing:
640 case MetisEmptyPartitionStrategy::TakeFromBiggestPartitionV1:
643 case MetisEmptyPartitionStrategy::TakeFromBiggestPartitionV2:
656 if (!cellUsedWithConstraints(item))
659 Int32 new_owner = CheckedConvert::toInt32(metis_part[index]);
661 changeCellOwner(item, cells_new_owner, new_owner);
668 cells_new_owner.synchronize();
701 for (
int i =0 ; i < nb_own_cell ; i++) {
702 elem_by_part[CheckedConvert::toInteger(metis_part[i])]++;
704 pm->
computeMinMaxSum(elem_by_part, min_part, max_part, sum_part, min_roc, max_proc);
707 Int32 max_part_id = -1;
708 Int32 max_part_nbr = -1;
710 for(
int i = 0; i < nb_part ; i++) {
711 if (sum_part[i] == 0) {
714 if(max_part_nbr < sum_part[i]) {
715 max_part_nbr = sum_part[i];
719 info() <<
"Parmetis: number empty parts " << nb_hole;
723 if(my_rank == max_proc[max_part_id]) {
725 for(
int i = 0; i < nb_part ; i++) {
727 if (sum_part[i] == 0 && offset < nb_own_cell) {
728 while(offset < nb_own_cell && metis_part[offset] != max_part_id) {
732 if(offset == nb_own_cell)
735 metis_part[offset] = i;
756 const Int32 nb_own_cell = metis_part.
size();
764 for (
int i =0 ; i < nb_own_cell ; i++) {
765 elem_by_part[metis_part[i]]++;
767 pm->
computeMinMaxSum(elem_by_part, min_part, max_part, sum_part, min_rank, max_rank);
772 Int32 max_part_id = -1;
773 Int64 max_part_nbr = -1;
775 Int64 total_nb_cell = 0;
776 for(
int i = 0; i < nb_part ; i++) {
777 Int64 current_sum_part = sum_part[i];
778 total_nb_cell += current_sum_part;
779 if (current_sum_part == 0) {
780 empty_part_ranks.
add(i);
783 if (max_part_nbr < current_sum_part) {
784 max_part_nbr = current_sum_part;
789 ARCANE_FATAL(
"Bad value max_part_id ({0})",max_part_id);
790 info() <<
"Parmetis: check empty parts: (" << algo_iteration <<
") nb_empty_parts=" << nb_hole
791 <<
" nb_max_part=" << max_part_nbr
792 <<
" max_part_rank=" << max_part_id
793 <<
" max_proc_max_part_id=" << max_rank[max_part_id]
794 <<
" empty_part_ranks=" << empty_part_ranks
795 <<
" total_nb_cell=" << total_nb_cell;
802 if (my_rank == max_rank[max_part_id]) {
804 Int32 max_remove_cell = nb_own_cell - 1;
806 for(
int i = 0; i < nb_part ; i++) {
808 if (sum_part[i] == 0 && offset < nb_own_cell) {
809 while (offset < max_remove_cell && metis_part[offset] != max_part_id) {
813 if (offset == max_remove_cell)
816 metis_part[offset] = i;
836 bool do_continue =
true;
837 Int32 last_nb_hole = 0;
840 info() <<
"Parmetis: nb_empty_partition=" << nb_hole <<
" last_nb_partition=" << last_nb_hole;
847 if (last_nb_hole>0 && last_nb_hole<=nb_hole){
848 pwarning() <<
"Can not remove all empty partitions. This is probably because you try"
849 <<
" to cut in too many partitions";
852 last_nb_hole = nb_hole;
875 bool have_ewgt =
false;
879 info() <<
"COMM_SIZE=" << commsize <<
" RANK=" << commrank;
887 info() <<
"MetisVtkDist=" << metis_vtkdist;
888 info() <<
"MetisXAdj =" << metis_xadj;
889 info() <<
"MetisAdjncy =" << metis_adjncy;
890 info() <<
"MetisVWgt =" << metis_vwgt;
891 info() <<
"MetisEWgt =" << metis_ewgt;
893#define METIS_ERROR ARCANE_FATAL("_writeGraph")
897 filename += commrank;
900 if (metis_vtkdist.
size() != commsize + 1)
903 nvtx = metis_vtkdist[commrank+1] - metis_vtkdist[commrank];
904 if (metis_xadj.
size() != nvtx + 1) {
905 std::cerr <<
"_writeGraph : nvtx+1 = " << nvtx <<
" != " << metis_xadj.
size() << std::endl;
910 nwgt = metis_vwgt.
size()/nvtx;
911 if (nwgt != 0 && metis_vwgt.
size() % nwgt != 0)
914 have_ewgt = (metis_ewgt.
size() != 0) ;
915 if (have_ewgt && metis_ewgt.
size() != metis_adjncy.
size())
921 Int64 localEdges = metis_xadj[nvtx];
924 file <<
"2" << std::endl;
925 file << commsize <<
"\t" << commrank << std::endl;
926 file << metis_vtkdist[commsize] <<
"\t" << globalEdges << std::endl;
927 file << nvtx <<
"\t" << localEdges << std::endl;
928 file <<
"0\t" <<
"0" << have_ewgt << nwgt << std::endl;
939 for (idxtype vertnum = 0 ; vertnum < nvtx ; vertnum++) {
941 for (
int dim = 0 ; dim < nwgt ; ++ dim)
942 file << metis_vwgt[vertnum*nwgt+dim] <<
'\t';
943 file << metis_xadj[vertnum + 1] - metis_xadj[vertnum];
944 for (idxtype edgenum = metis_xadj[vertnum] ;
945 edgenum < metis_xadj[vertnum + 1] ; ++edgenum) {
947 file <<
'\t' << metis_ewgt[edgenum];
948 file <<
'\t' << metis_adjncy[edgenum];
969#if ARCANE_DEFAULT_PARTITIONER == METIS_DEFAULT_PARTITIONER
#define ARCANE_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro pour déclarer une interface lors de l'enregistrement d'un service.
Integer size() const
Nombre d'éléments du vecteur.
ArcaneMetisMeshPartitionerObject(const Arcane::ServiceBuildInfo &sbi)
Constructeur.
CaseOptionsMetisMeshPartitioner * options() const
Options du jeu de données du service.
Exception lorsqu'un argument est invalide.
Conversion d'un tableau d'un type vers un autre type.
Vue modifiable d'un tableau d'un type T.
constexpr Integer size() const noexcept
Retourne la taille du tableau.
void fill(const DataType &data)
Remplissage du tableau.
ConstArrayView< T > constView() const
Vue constante sur ce tableau.
void resize(Int64 s)
Change le nombre d'éléments du tableau à s.
void reserve(Int64 new_capacity)
Réserve le mémoire pour new_capacity éléments.
const T * data() const
Accès à la racine du tableau hors toute protection.
iterator begin()
Itérateur sur le premier élément du tableau.
void add(ConstReferenceType val)
Ajoute l'élément val à la fin du tableau.
Int32 nbEdge() const
Nombre d'arêtes de la maille.
Int32 nbFace() const
Nombre de faces de la maille.
Int32 globalIteration() const
Numéro de l'itération courante.
Vue constante d'un tableau de type T.
Classe template pour convertir un type.
Classe permettant d'activer/désactiver temporairement les exceptions flottantes du processeur.
Redistribute graph data to another "communicator".
void initWithOneRankPerNode(bool allow_only_one_rank)
Automatic distribution : one partitioning process per node.
Interface d'un partitionneur de maillage.
Interface d'un partitionneur de maillage.
Interface du gestionnaire de parallélisme pour un sous-domaine.
virtual bool isThreadImplementation() const =0
Indique si l'implémentation utilise les threads.
virtual void computeMinMaxSum(char val, char &min_val, char &max_val, char &sum_val, Int32 &min_rank, Int32 &max_rank)=0
Calcule en une opération la somme, le min, le max d'une valeur.
virtual Int32 commRank() const =0
Rang de cette instance dans le communicateur.
virtual Int32 commSize() const =0
Nombre d'instance dans le communicateur.
virtual void allGather(ConstArrayView< char > send_buf, ArrayView< char > recv_buf)=0
Effectue un regroupement sur tous les processeurs. Il s'agit d'une opération collective....
virtual Parallel::Communicator communicator() const =0
Communicateur MPI associé à ce gestionnaire.
virtual char reduce(eReduceType rt, char v)=0
Effectue la réduction de type rt sur le réel v et retourne la valeur.
virtual void barrier()=0
Effectue une barière.
Interface du gestionnaire d'un sous-domaine.
virtual const CommonVariables & commonVariables() const =0
Informations sur les variables standards.
virtual void flush()=0
Flush tous les flots.
@ PNoDump
Indique que la variable ne doit pas être sauvegardée.
constexpr bool hasFlags(Int32 flags) const
Retourne si les flags flags sont positionnées pour l'entité
Real imbalance() const override
Déséquilibre de temps de calcul.
Real maxImbalance() const override
Déséquilibre maximal autorisé
virtual void changeOwnersFromCells()
Positionne les nouveaux propriétaires des noeuds, arêtes et faces à partir des mailles.
Partitioneur de maillage utilisant la bibliothèque PARMetis.
void partitionMesh(bool initial_partition) override
void _removeEmptyPartsV2(Int32 nb_part, ArrayView< idxtype > metis_part)
Comble les partitions vides (version 2).
Int32 _removeEmptyPartsV2Helper(Int32 nb_part, ArrayView< idxtype > metis_part, Int32 algo_iteration)
Applique une itération de l'algorithme de suppression des partitions vides.
void build() override
Construction de niveau build du service.
void _partitionMesh(bool initial_partition, Int32 nb_part)
int _writeGraph(IParallelMng *pm, Span< const idxtype > metis_vtkdist, Span< const idxtype > metis_xadj, Span< const idxtype > metis_adjncy, Span< const idxtype > metis_vwgt, Span< const idxtype > metis_ewgt, const String &Name) const
void _removeEmptyPartsV1(Int32 nb_part, Int32 nb_own_cell, ArrayView< idxtype > metis_part)
Comble les partitions vides (version 1).
Wrapper autour des appels de Parmetis.
int callPartKway(const bool print_digest, const bool gather, idx_t *vtxdist, idx_t *xadj, idx_t *adjncy, idx_t *vwgt, idx_t *adjwgt, idx_t *wgtflag, idx_t *numflag, idx_t *ncon, idx_t *nparts, real_t *tpwgts, real_t *ubvec, idx_t *options, idx_t *edgecut, idx_t *part)
Simple wrapper autour de la routine ParMetis "ParMETIS_V3_PartKway".
int callAdaptiveRepart(const bool print_digest, const bool gather, idx_t *vtxdist, idx_t *xadj, idx_t *adjncy, idx_t *vwgt, idx_t *vsize, idx_t *adjwgt, idx_t *wgtflag, idx_t *numflag, idx_t *ncon, idx_t *nparts, real_t *tpwgts, real_t *ubvec, real_t *ipc2redist, idx_t *options, idx_t *edgecut, idx_t *part)
Simple wrapper autour de la routine ParMetis "ParMETIS_V3_AdaptiveRepart".
Conversion d'un tableau de flottants vers un tableau d'entiers/longs. \abstract Cette classe gere le ...
Structure contenant les informations pour créer un service.
Propriétés de création d'un service.
Vecteur 1D de données avec sémantique par référence.
constexpr __host__ __device__ SizeType size() const noexcept
Retourne la taille du tableau.
Vue d'un tableau d'éléments de type T.
Constructeur de chaîne de caractère unicode.
String toString() const
Retourne la chaîne de caractères construite.
Chaîne de caractères unicode.
bool null() const
Retourne true si la chaîne est nulle.
String upper() const
Transforme tous les caractères de la chaîne en majuscules.
const char * localstr() const
Retourne la conversion de l'instance dans l'encodage UTF-8.
Postionne le nom de l'action en cours d'exécution.
TraceMessage info() const
Flot pour un message d'information.
ITraceMng * traceMng() const
Gestionnaire de trace.
TraceMessage pwarning() const
Vecteur 1D de données avec sémantique par valeur (style STL).
Paramètres nécessaires à la construction d'une variable.
ItemGroupT< Cell > CellGroup
Groupe de mailles.
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro pour enregistrer un service.
MeshVariableScalarRefT< Cell, Integer > VariableCellInteger
Grandeur au centre des mailles de type entier.
ItemVariableScalarRefT< Int32 > VariableItemInt32
Grandeur de type entier 32 bits.
Integer toInteger(Real r)
Converti un Real en Integer.
@ ReduceSum
Somme des valeurs.
@ ReduceMax
Maximum des valeurs.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
UniqueArray< Int64 > Int64UniqueArray
Tableau dynamique à une dimension d'entiers 64 bits.
std::int64_t Int64
Type entier signé sur 64 bits.
Int32 Integer
Type représentant un entier.
@ ST_SubDomain
Le service s'utilise au niveau du sous-domaine.
@ IK_Cell
Entité de maillage de genre maille.
double Real
Type représentant un réel.
std::int32_t Int32
Type entier signé sur 32 bits.