1305 lines
63 KiB
C++
1305 lines
63 KiB
C++
|
|
// This file is part of the Herezh++ application.
|
|
//
|
|
// The finite element software Herezh++ is dedicated to the field
|
|
// of mechanics for large transformations of solid structures.
|
|
// It is developed by Gérard Rio (APP: IDDN.FR.010.0106078.000.R.P.2006.035.20600)
|
|
// INSTITUT DE RECHERCHE DUPUY DE LÔME (IRDL) <https://www.irdl.fr/>.
|
|
//
|
|
// Herezh++ is distributed under GPL 3 license ou ultérieure.
|
|
//
|
|
// Copyright (C) 1997-2022 Université Bretagne Sud (France)
|
|
// AUTHOR : Gérard Rio
|
|
// E-MAIL : gerardrio56@free.fr
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License,
|
|
// or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
// For more information, please consult: <https://herezh.irdl.fr/>.
|
|
|
|
|
|
#include "Charge.h"
|
|
#include "ConstMath.h"
|
|
#include "MathUtil.h"
|
|
#include "ReferenceNE.h"
|
|
#include "ReferenceAF.h"
|
|
#include "CourbePolyLineaire1D.h"
|
|
|
|
// --------------- méthodes particulière pour les conteneurs de chargement ----------
|
|
// == cas de la classe PHydro
|
|
// certaines fonctions sont redéfinies en suivant le caneva de Bloc.h
|
|
Charge::PHydro::PHydro()
|
|
:BlocGeneEtVecMultType(2,1),N(),M(),normer(false)
|
|
{};
|
|
Charge::PHydro::PHydro(const Charge::PHydro & a) : // constructeur par defaut
|
|
BlocGeneEtVecMultType(a),N(a.N),M(a.M),normer(a.normer)
|
|
{};
|
|
Charge::PHydro::~PHydro () // destructeur
|
|
{}; // les courbes éventuelles existent par ailleurs, elles n'ont pas à être supprimées
|
|
// mise en place de l'association des courbes
|
|
// en retour indique si l'association est ok
|
|
bool Charge::PHydro::Mise_en_place_des_courbes(LesCourbes1D& lesCourbes1D)
|
|
{ // on passe en revue la liste de nom pour associer éventuellement avec les courbes
|
|
int dim = ParaGlob::Dimension();
|
|
// cas du vecteur N
|
|
for (int i=1;i<=dim;i++)
|
|
{ const string& nom = Nom_vect(1,i);
|
|
if (nom != "NULL")
|
|
{ // cas d'une coordonnée gérée par une courbe
|
|
if (N.Taille() == 0) N.Change_taille(dim,NULL);
|
|
if (lesCourbes1D.Existe(nom))
|
|
{ N(i) = lesCourbes1D.Trouve(nom);}
|
|
else
|
|
{ cout << "\n erreur, on ne trouve pas la courbe coefficient " << i << "pour la direction normale N !!! "
|
|
<< " de nom: " << nom
|
|
<< "\n Charge::PHydro::Mise_en_place_des_courbes(...";
|
|
return false;
|
|
};
|
|
};
|
|
};
|
|
// cas du vecteur M
|
|
for (int i=1;i<=dim;i++)
|
|
{ const string& nom = Nom_vect(2,i);
|
|
if (nom != "NULL")
|
|
{ // cas d'une coordonnée gérée par une courbe
|
|
if (M.Taille() == 0) M.Change_taille(dim,NULL);
|
|
if (lesCourbes1D.Existe(nom))
|
|
{ M(i) = lesCourbes1D.Trouve(nom);}
|
|
else
|
|
{ cout << "\n erreur, on ne trouve pas la courbe coefficient " << i << "pour le point A de la surface libre !!! "
|
|
<< " de nom: " << nom
|
|
<< "\n Charge::PHydro::Mise_en_place_des_courbes(...";
|
|
return false;
|
|
};
|
|
};
|
|
};
|
|
// si on arrive là c'est que tous c'est bien passé
|
|
return true;
|
|
};
|
|
// récup de la direction N normée
|
|
const Coordonnee& Charge::PHydro::Direction_N()
|
|
{ // on regarde s'il y a des courbes de charges
|
|
Coordonnee N_vect = Vect_de_coordonnee(1); // init
|
|
if (N.Taille() == 0)
|
|
// pas de courbe, on norme la première fois
|
|
{ if (!normer)
|
|
{ double norme = N_vect.Norme();
|
|
if (norme != 1.)
|
|
{ N_vect.Normer(); Change_vect_de_coordonnee(1,N_vect);}
|
|
normer = true;
|
|
};
|
|
}
|
|
else // sinon il faut voir avec le temps
|
|
{int dim = N.Taille();
|
|
// récup des valeurs
|
|
for (int i=1;i <= dim; i++)
|
|
{if (N(i) != NULL)
|
|
N_vect(i) = (N(i)->Valeur(ParaGlob::Variables_de_temps().TempsCourant()));
|
|
};
|
|
// on norme le vecteur
|
|
double norme = N_vect.Norme();
|
|
if (norme != 1.)
|
|
{ N_vect.Normer();};
|
|
// on met à jour le vecteur
|
|
Change_vect_de_coordonnee(1,N_vect);
|
|
};
|
|
// retour du vecteur qui continue à exister
|
|
return Vect_de_coordonnee(1);
|
|
};
|
|
// récup du point de la surface libre
|
|
const Coordonnee& Charge::PHydro::Point_M()
|
|
{ // on regarde s'il y a des courbes de charges
|
|
Coordonnee M_vect = Vect_de_coordonnee(2); // init
|
|
if (M.Taille() != 0)
|
|
// il faut voir avec le temps
|
|
{int dim = M.Taille();
|
|
// récup des valeurs
|
|
for (int i=1;i <= dim; i++)
|
|
{if (M(i) != NULL)
|
|
M_vect(i) = (M(i)->Valeur(ParaGlob::Variables_de_temps().TempsCourant()));
|
|
};
|
|
// on met à jour le vecteur
|
|
Change_vect_de_coordonnee(2,M_vect);
|
|
};
|
|
// retour du vecteur qui continue à exister
|
|
return Vect_de_coordonnee(2);
|
|
};
|
|
|
|
// == cas de la classe PHydrodyna
|
|
// --- mise en place de l'association des courbes pour PHydrodyna
|
|
// en retour indique si l'association est ok
|
|
bool Charge::PHydrodyna::Mise_en_place_des_courbes(LesCourbes1D& lesCourbes1D)
|
|
{ // on passe en revue la liste de nom pour associer avec les courbes
|
|
// cas du frottement fluid
|
|
const string& nom = Nom(3);
|
|
if (nom != "NULL")
|
|
{ // cas où le frottement fluide est actif
|
|
if (lesCourbes1D.Existe(nom))
|
|
{ frot_fluid = lesCourbes1D.Trouve(nom);}
|
|
else
|
|
{ cout << "\n erreur, on ne trouve pas la courbe coefficient pour le frottement fluide !!! "
|
|
<< " de nom: " << nom
|
|
<< "\n Charge::PHydrodyna::Mise_en_place_des_courbes(...";
|
|
return false;
|
|
};
|
|
};
|
|
// cas de l'effort de traînée
|
|
const string& nom1 = Nom(4);
|
|
if (nom1 != "NULL")
|
|
{ // cas où l'effort de traînée est actif
|
|
if (lesCourbes1D.Existe(nom1))
|
|
{ coef_aero_n = lesCourbes1D.Trouve(nom1);}
|
|
else
|
|
{ cout << "\n erreur, on ne trouve pas la courbe coefficient pour l'effort de trainee !!! "
|
|
<< " de nom: " << nom1
|
|
<< "\n Charge::PHydrodyna::Mise_en_place_des_courbes(...";
|
|
return false;
|
|
};
|
|
};
|
|
// cas de l'effort de portance
|
|
const string& nom2 = Nom(5);
|
|
if (nom2 != "NULL")
|
|
{ // cas où l'effort de portance est actif
|
|
if (lesCourbes1D.Existe(nom2))
|
|
{ coef_aero_t = lesCourbes1D.Trouve(nom2);}
|
|
else
|
|
{ cout << "\n erreur, on ne trouve pas la courbe coefficient pour l'effort de portance !!! "
|
|
<< " de nom: " << nom2
|
|
<< "\n Charge::PHydrodyna::Mise_en_place_des_courbes(...";
|
|
return false;
|
|
};
|
|
};
|
|
// si on arrive là c'est que tous c'est bien passé
|
|
return true;
|
|
};
|
|
|
|
// cas des torseurs de charges ponctuelles
|
|
|
|
|
|
// --------------- fin méthodes particulière pour les conteneurs de chargement ----------
|
|
|
|
// ------------- fin des variables static -----------------
|
|
|
|
Charge::Charge () : // constructeur par defaut
|
|
tabFsurfac(),tabPresUnif(),tabPonctuel(),PresUniDir(),PresHydro(),coefHydroDyna(),tabFlineique()
|
|
,tabFlineiqueSuiv(),tabFvol(),f_charge(NULL),interne_f_charge(false)
|
|
,tabTorseurPonct(),tab_P(),t_force()
|
|
,multi(0.),coeff(0.),temps_sauve(0.),multi_sauve(0.),coeff_sauve(0.)
|
|
,temps_fin_non_stricte(0)
|
|
,nomtypeCharge(),ancien_num_pt_type5(0),num_pt_courant_type5(0)
|
|
,temps_cpu_chargement()
|
|
{// mise à jour de la valeur par défaut du chargement au cas où aucun chargement
|
|
// n'est définit
|
|
nomtypeCharge ="TYPE1";
|
|
tabType.Change_taille(1);
|
|
tabType(1) = 1.;
|
|
}
|
|
Charge::~Charge ()
|
|
{ if (f_charge != NULL)
|
|
{ // on utilise un deuxième indicateur, car si la courbe est globale, et quelle est déjà supprimée
|
|
// on ne peut plus la tester
|
|
if (interne_f_charge)
|
|
delete f_charge;
|
|
};
|
|
};
|
|
|
|
// affichage des differents chargements
|
|
void Charge::Affiche() const // affiche la totalite
|
|
{ Affiche1();
|
|
Affiche2();
|
|
};
|
|
void Charge::Affiche1() const // affiche que la premiere lecture
|
|
{ cout << "\n ******** Charges ******** \n";
|
|
if (tabFvol.Taille() != 0)
|
|
{cout << tabFvol.Taille() << " reference de force volumique lue \n";
|
|
for (int i=1; i<=tabFvol.Taille(); i++)
|
|
tabFvol(i).Affiche();
|
|
cout << "\n\n";
|
|
}
|
|
if (tabFsurfac.Taille() != 0)
|
|
{cout << tabFsurfac.Taille() << " reference de densite de force surfacique lue \n";
|
|
for (int i=1; i<=tabFsurfac.Taille(); i++)
|
|
tabFsurfac(i).Affiche();
|
|
cout << "\n\n";
|
|
}
|
|
if (tabPresUnif.Taille() != 0)
|
|
{cout << tabPresUnif.Taille() << " reference de pression uniformement reparti lue \n";
|
|
for (int i=1; i<=tabPresUnif.Taille(); i++)
|
|
tabPresUnif(i).Affiche();
|
|
cout << "\n\n";
|
|
}
|
|
if (tabPonctuel.Taille() != 0)
|
|
{cout << tabPonctuel.Taille() << " reference de force ponctuelle lue \n";
|
|
for (int i=1; i<=tabPonctuel.Taille(); i++)
|
|
tabPonctuel(i).Affiche();
|
|
cout << "\n\n";
|
|
}
|
|
if (PresUniDir.Taille() != 0)
|
|
{cout << PresUniDir.Taille() << " reference de pression unidirectionnelle"
|
|
<< " type pression dynamique des fluides lue \n";
|
|
for (int i=1; i<=PresUniDir.Taille(); i++)
|
|
PresUniDir(i).Affiche();
|
|
cout << "\n\n";
|
|
}
|
|
if (PresHydro.Taille() != 0)
|
|
{cout << PresHydro.Taille() << " reference de pression hydrostatique lue \n";
|
|
for (int i=1; i<=PresHydro.Taille(); i++)
|
|
PresHydro(i).Affiche();
|
|
cout << "\n\n";
|
|
}
|
|
if (coefHydroDyna.Taille() != 0)
|
|
{cout << coefHydroDyna.Taille() << " reference de pression hydrodynamique lue \n";
|
|
for (int i=1; i<=coefHydroDyna.Taille(); i++)
|
|
coefHydroDyna(i).Affiche();
|
|
cout << "\n\n";
|
|
}
|
|
if (tabFlineique.Taille() != 0)
|
|
{cout << tabFlineique.Taille() << " reference de densite de force lineique lue \n";
|
|
for (int i=1; i<=tabFlineique.Taille(); i++)
|
|
tabFlineique(i).Affiche();
|
|
cout << "\n\n";
|
|
};
|
|
if (tabFlineiqueSuiv.Taille() != 0)
|
|
{cout << tabFlineiqueSuiv.Taille() << " reference de densite de force lineique suiveuse lue \n";
|
|
for (int i=1; i<=tabFlineiqueSuiv.Taille(); i++)
|
|
tabFlineiqueSuiv(i).Affiche();
|
|
cout << "\n\n";
|
|
};
|
|
if (tabTorseurPonct.Taille() != 0)
|
|
{cout << tabTorseurPonct.Taille() << " reference de torseur de forces ponctuelles lue \n";
|
|
for (int i=1; i<=tabTorseurPonct.Taille(); i++)
|
|
tabTorseurPonct(i).Affiche();
|
|
cout << "\n\n";
|
|
};
|
|
|
|
cout << endl;
|
|
};
|
|
void Charge::Affiche2() const // affiche que la seconde lecture
|
|
{ cout << "\n ******** Type de Chargement ******** \n";
|
|
if ((nomtypeCharge == "TYPE1") || (nomtypeCharge == "TYPE2") )
|
|
{ cout << " type de chargement : " << nomtypeCharge << ", parametre : ";
|
|
for (int i=1; i<=tabType.Taille(); i++)
|
|
cout << tabType(i) << " " ;
|
|
cout << endl;
|
|
}
|
|
else if (nomtypeCharge == "TYPE3")
|
|
{ cout << " type de chargement : " << nomtypeCharge << ", pas de parametre " << endl ;}
|
|
else if ((nomtypeCharge == "TYPE4") || (nomtypeCharge == "TYPE5"))
|
|
{ cout << " type de chargement : " << nomtypeCharge << ", fonction de charge: ";
|
|
if (interne_f_charge)
|
|
{f_charge->Affiche();}
|
|
else { cout << " courbe1D " << f_charge->NomCourbe() << " ";};
|
|
}
|
|
else
|
|
{ cout << " **** type de chargement non défini !! **** " << endl ;}
|
|
// cas de l'application du contrôle de temps fin
|
|
if (temps_fin_non_stricte)
|
|
cout << "\n la limite de temps est non stricte ";
|
|
else
|
|
cout << "\n la limite de temps est stricte ";
|
|
};
|
|
|
|
// lecture des actions exterieurs imposees
|
|
void Charge::Lecture1(UtilLecture & entreePrinc,LesReferences& lesRef,LesCourbes1D& lesCourbes1D
|
|
,LesFonctions_nD& lesFonctionsnD)
|
|
{ if (ParaGlob::NiveauImpression() >= 4)
|
|
cout << " debut de la lecture du chargement " << endl;
|
|
MotCle motCle; // ref aux mots cle
|
|
{ // on se positionne sur un mot cle
|
|
while ( !motCle.SimotCle(entreePrinc.tablcar))
|
|
entreePrinc.NouvelleDonnee();
|
|
// definition de la liste de sous mot cle specifique
|
|
Tableau<string> tsousMotref(10);
|
|
tsousMotref(1) = "UNIFORME";tsousMotref(2) = "PRESSION";
|
|
tsousMotref(3) = "PONCTUELLE";tsousMotref(4) = "PRESSDIR";
|
|
tsousMotref(5) = "PHYDRO"; tsousMotref(6) = "LINEIQUE";
|
|
tsousMotref(7) = "VOLUMIQUE"; tsousMotref(8) = "LINEIC_SUIVEUSE";
|
|
tsousMotref(9) = "P_HYDRODYNA"; tsousMotref(10) = "TORSEUR_PONCT";
|
|
Tableau<string> tsousMot(tsousMotref); // copie qui varie selon l'utilisation
|
|
|
|
// lecture pour se positionner sur un premier sous mot cle
|
|
entreePrinc.NouvelleDonnee();
|
|
|
|
// def d'une classe de lecture de bloc BlocCharge<BlocVecType>
|
|
LectBloc < BlocCharge< BlocDdlLim<BlocForces> > > lecVecType;
|
|
// def d'une classe de lecture de bloc BlocScalType
|
|
LectBloc < BlocCharge< BlocDdlLim<BlocIntensite> > > lecScalType;
|
|
// def d'une classe de lecture de bloc PHydro
|
|
LectBloc < BlocCharge< BlocDdlLim<PHydro> > > lecPHydro;
|
|
// def d'une classe de lecture de bloc PHydrodyna
|
|
LectBloc < BlocCharge< BlocDdlLim<PHydrodyna> > > lecPHydrodyna;
|
|
// def d'une classe de lecture de bloc PTorseurPonct
|
|
LectBloc < BlocCharge< BlocDdlLim<PTorseurPonct> > > lecPTorseurPonct;
|
|
|
|
bool avance = true;
|
|
while ( !motCle.SimotCle(entreePrinc.tablcar) && avance)
|
|
// lecture jusqu'au prochain mot cle
|
|
{ string inter(entreePrinc.tablcar); // pour eviter la boucle infini qui n'avance pas
|
|
// lecture eventuelle des densite de force surfacique
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (1); // on enleve "UNIFORME"
|
|
lecVecType.Lecture(entreePrinc,lesRef,"UNIFORME",
|
|
"lecture des densite de force surfacique",tabFsurfac,tsousMot,false);
|
|
|
|
// lecture eventuelle des pressions uniformement reparti
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (2); // on enleve "PRESSION"
|
|
lecScalType.Lecture(entreePrinc,lesRef,"PRESSION",
|
|
"lecture des pressions uniformement reparti",tabPresUnif,tsousMot,false);
|
|
|
|
// lecture eventuelle des forces ponctuelles
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (3); // on enleve "PONCTUELLE"
|
|
lecVecType.Lecture(entreePrinc,lesRef,"PONCTUELLE",
|
|
"lecture des forces ponctuelles",tabPonctuel,tsousMot,false);
|
|
|
|
// lecture eventuelle des pression unidirectionnelle
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (4); // on enleve "PRESSDIR"
|
|
lecVecType.Lecture(entreePrinc,lesRef,"PRESSDIR",
|
|
"lecture des pressions unidirectionnelles",PresUniDir,tsousMot,false);
|
|
|
|
// lecture eventuelle des pressions hydrostatiques
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (5); // on enleve "PHYDRO"
|
|
lecPHydro.Lecture(entreePrinc,lesRef,"PHYDRO",
|
|
"lecture des pressions hydrostatiques",PresHydro,tsousMot,false);
|
|
// initialisation éventuelle
|
|
int taj= PresHydro.Taille(); for (int i=1;i<=taj;i++)
|
|
{ if (!(PresHydro(i).Mise_en_place_des_courbes(lesCourbes1D))) // mise en place et test
|
|
{ entreePrinc.MessageBuffer("** lecture des pressions hydrostatiques **");
|
|
throw (UtilLecture::ErrNouvelleDonnee(-1));
|
|
Sortie (1);
|
|
};
|
|
};
|
|
|
|
// lecture eventuelle des coeffs pour les efforts hydrodynamiques
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (9); // on enleve "P_HYDRODYNA"
|
|
lecPHydrodyna.Lecture(entreePrinc,lesRef,"P_HYDRODYNA",
|
|
"lecture des pressions hydrodynamique",coefHydroDyna,tsousMot,false);
|
|
// initialisation éventuelle
|
|
int tai= coefHydroDyna.Taille(); for (int i=1;i<=tai;i++)
|
|
{ if (!(coefHydroDyna(i).Mise_en_place_des_courbes(lesCourbes1D))) // mise en place et test
|
|
{ entreePrinc.MessageBuffer("** lecture des pressions hydrodynamiques **");
|
|
throw (UtilLecture::ErrNouvelleDonnee(-1));
|
|
Sortie (1);
|
|
};
|
|
};
|
|
|
|
// lecture eventuelle des forces linéique uniformement reparti
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (6); // on enleve "LINEIQUE"
|
|
lecVecType.Lecture(entreePrinc,lesRef,"LINEIQUE",
|
|
"lecture des forces lineique uniformement reparti",tabFlineique,tsousMot,false);
|
|
|
|
// lecture eventuelle des forces linéique uniformement reparti suiveuses
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (8); // on enleve "LINEIC_SUIVEUSE"
|
|
lecVecType.Lecture(entreePrinc,lesRef,"LINEIC_SUIVEUSE",
|
|
"lecture des forces lineique suiveuses uniformement reparti",tabFlineiqueSuiv,tsousMot,false);
|
|
|
|
// lecture eventuelle des forces volumiques
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (7); // on enleve "VOLUMIQUE"
|
|
lecVecType.Lecture(entreePrinc,lesRef,"VOLUMIQUE",
|
|
"lecture des forces volumique",tabFvol,tsousMot,false);
|
|
|
|
|
|
// lecture eventuelle des torseurs de forces ponctuelles
|
|
tsousMot = tsousMotref;
|
|
tsousMot.Enleve (10); // on enleve "TORSEUR_PONCT"
|
|
lecPTorseurPonct.Lecture(entreePrinc,lesRef,"TORSEUR_PONCT",
|
|
"lecture des torseurs de forces ponctuelles ",tabTorseurPonct,tsousMot,false);
|
|
|
|
string sortie(entreePrinc.tablcar); // pour eviter la boucle infini
|
|
if (inter == sortie)
|
|
{avance = false;
|
|
cout << " erreur dans la lecture des chargements, on ne trouve pas"
|
|
<< " de sous mot cle";
|
|
entreePrinc.MessageBuffer("** lecture des divers chargements **");
|
|
Affiche1();
|
|
throw (UtilLecture::ErrNouvelleDonnee(-1));
|
|
Sortie (1);
|
|
}
|
|
}
|
|
|
|
}
|
|
if (ParaGlob::NiveauImpression() >= 4)
|
|
cout << " fin de la lecture du chargement " << endl;
|
|
// on dimensionne certains tableaux de travail
|
|
int taille_tors = tabTorseurPonct.Taille();
|
|
tab_P.Change_taille(taille_tors);
|
|
t_force.Change_taille(taille_tors);
|
|
};
|
|
|
|
// lecture du type d'application du chargement
|
|
void Charge::Lecture2(UtilLecture & entreePrinc,LesCourbes1D& lesCourbes1D
|
|
,LesFonctions_nD& lesFonctionsnD)
|
|
{ if (ParaGlob::NiveauImpression() >= 4)
|
|
cout << " debut de la lecture du type d'application du chargement " << endl;
|
|
MotCle motCle; // ref aux mots cle
|
|
while ( !motCle.SimotCle(entreePrinc.tablcar))
|
|
entreePrinc.NouvelleDonnee();
|
|
if (strstr(entreePrinc.tablcar,"typecharge")!=NULL)
|
|
{ entreePrinc.NouvelleDonnee();
|
|
*(entreePrinc.entree) >> nomtypeCharge;
|
|
if (nomtypeCharge == "TYPE1")
|
|
{ tabType.Change_taille(1);
|
|
*(entreePrinc.entree) >> tabType(1);
|
|
// verif de la coherence des donnees
|
|
if ( (Dabs(tabType(1)) <= ConstMath::trespetit) )
|
|
{ cout << " \n erreur : type de chargement 1, le temps final lu "
|
|
<< tabType(1) << " est trop petit " ;
|
|
entreePrinc.MessageBuffer("** lecture du type d\'application du chargement **");
|
|
Affiche2();
|
|
throw (UtilLecture::ErrNouvelleDonnee(-1));
|
|
Sortie (1);
|
|
}
|
|
}
|
|
else if (nomtypeCharge == "TYPE2")
|
|
{ tabType.Change_taille(2);
|
|
for (int i=1; i<=2; i++)
|
|
*(entreePrinc.entree) >> tabType(i);
|
|
// verif de la coherence des donnees
|
|
if ( (Dabs(tabType(1)) <= ConstMath::trespetit) )
|
|
{ cout << " \n erreur : type de chargement 2, le temps t1 lu "
|
|
<< tabType(1) << " est trop petit " ;
|
|
entreePrinc.MessageBuffer("** lecture du type d\'application du chargement **");
|
|
Affiche2();
|
|
throw (UtilLecture::ErrNouvelleDonnee(-1));
|
|
Sortie (1);
|
|
}
|
|
if ( (Dabs(tabType(2)) <= ConstMath::trespetit) )
|
|
{ cout << " \n erreur : type de chargement 2, le temps t2 lu "
|
|
<< tabType(2) << " est trop petit " ;
|
|
entreePrinc.MessageBuffer("** lecture du type d\'application du chargement **");
|
|
Affiche2();
|
|
throw (UtilLecture::ErrNouvelleDonnee(-1));
|
|
Sortie (1);
|
|
}
|
|
}
|
|
else if (nomtypeCharge == "TYPE3")
|
|
{ // rien à faire
|
|
}
|
|
else if ((nomtypeCharge == "TYPE4") || (nomtypeCharge == "TYPE5"))
|
|
{ // cas d'une courbe de charge donnée par une courbe1D
|
|
string nom;
|
|
*(entreePrinc.entree) >> nom; // lecture du nom de la courbe
|
|
// on regarde si la courbe existe, si oui on récupère la référence
|
|
if (lesCourbes1D.Existe(nom))
|
|
{ f_charge = lesCourbes1D.Trouve(nom);
|
|
interne_f_charge = false;
|
|
}
|
|
else
|
|
{ // sinon il faut la lire maintenant
|
|
string non_courbe("_");
|
|
f_charge = Courbe1D::New_Courbe1D(non_courbe,Id_Nom_Courbe1D (nom.c_str()));
|
|
// lecture de la courbe
|
|
f_charge->LectDonnParticulieres_courbes (non_courbe,&entreePrinc);
|
|
interne_f_charge = true;
|
|
};
|
|
// dans le cas ou le type est 5, on vérifie que la courbe est de type de liste de points
|
|
if (nomtypeCharge == "TYPE5")
|
|
{ if ( (f_charge->Type_courbe() != COURBEPOLYLINEAIRE_1_D)
|
|
&& (f_charge->Type_courbe() != CPL1D))
|
|
{cout << "\n **** erreur en lecture de la courbe de charge associee au type TYPE5"
|
|
<< "\n la courbe doit etre de type poly-lineaire classique ou simplifie (cf. doc) ";
|
|
entreePrinc.MessageBuffer("** erreur en lecture du type de chargement **");
|
|
throw (UtilLecture::ErrNouvelleDonnee(-1));
|
|
Sortie (1);
|
|
};
|
|
};
|
|
}
|
|
else if (nomtypeCharge == "TYPE6")
|
|
{ entreePrinc.NouvelleDonnee();
|
|
typedef double inter ;
|
|
list <inter> lili;
|
|
inter t1,t2;
|
|
while ( !motCle.SimotCle(entreePrinc.tablcar))
|
|
{ *(entreePrinc.entree) >> t1 >> t2;
|
|
lili.push_back(t1);
|
|
lili.push_back(t2);
|
|
entreePrinc.NouvelleDonnee();
|
|
}
|
|
// enregistrement des infos
|
|
tabType.Change_taille((int) lili.size());
|
|
list <inter>::iterator i;
|
|
int j;
|
|
for (i=lili.begin(),j=1 ; i != lili.end(); i++,j++)
|
|
tabType(j) = (*i);
|
|
}
|
|
else if (nomtypeCharge == "TYPE7")
|
|
{ entreePrinc.NouvelleDonnee();
|
|
typedef double inter ;
|
|
list <inter> lili;
|
|
inter t1,t2;
|
|
while ( !motCle.SimotCle(entreePrinc.tablcar))
|
|
{ *(entreePrinc.entree) >> t1 >> t2;
|
|
lili.push_back(t1);
|
|
lili.push_back(t2);
|
|
entreePrinc.NouvelleDonnee();
|
|
}
|
|
// enregistrement des infos
|
|
tabType.Change_taille((int) lili.size());
|
|
list <inter>::iterator i;
|
|
int j;
|
|
for (i=lili.begin(),j=1 ; i != lili.end(); i++,j++)
|
|
tabType(j) = (*i);
|
|
}
|
|
|
|
}
|
|
else
|
|
{ if (ParaGlob::NiveauImpression() >= 4)
|
|
{ cout << " \n warning : pas de mot cle typecharge, utilisation des valeurs par defaut ";
|
|
Affiche2();}
|
|
}
|
|
if (ParaGlob::NiveauImpression() >= 4)
|
|
cout << " fin de la lecture du du type d'application du chargement " << endl;
|
|
};
|
|
|
|
// initialisation du chargement
|
|
// on verifie egalement la bonne adequation des references
|
|
void Charge::Initialise(LesMaillages * lesmail,LesReferences* lesRef,ParaAlgoControle& pa
|
|
,const LesCourbes1D& lesCourbes1D,const LesFonctions_nD& lesFonctionsnD)
|
|
{ // initialisation des parametres temps
|
|
paAlgo = & pa; // paAlgo->Multiplicateur()
|
|
//-// deltatmaxi = pa.Deltatmaxi() ; // increment de temps maxi
|
|
//-// prectemps = pa.Prectemps(); // precision sur le temps final
|
|
//-// deltat = pa.Deltat(); // increment de temps
|
|
//-// tempsfin = pa.Tempsfin(); // temps de fin de calcul
|
|
//-// maxincre = pa.Maxincre(); // maximum d'increments de temps
|
|
//-// max_essai_incre = pa.Max_essai_incre(); // maximum de tentatives d'increments de temps
|
|
//-// multiplicateur = pa.Multiplicateur(); // multiplicateur de la charge
|
|
// initialisation des pointeurs de fonction
|
|
if (nomtypeCharge == "TYPE1")
|
|
{ PtDebut = &Charge::Debut1;
|
|
PtAvance = &Charge::Avance1;
|
|
PtPrecedent = &Charge::Precedent1;
|
|
PtFin = &Charge::Fin1;
|
|
}
|
|
else if (nomtypeCharge == "TYPE2")
|
|
{ PtDebut = &Charge::Debut2;
|
|
PtAvance = &Charge::Avance2;
|
|
PtPrecedent = &Charge::Precedent2;
|
|
PtFin = &Charge::Fin2;
|
|
}
|
|
else if (nomtypeCharge == "TYPE3")
|
|
{ PtDebut = &Charge::Debut3;
|
|
PtAvance = &Charge::Avance3;
|
|
PtPrecedent = &Charge::Precedent3;
|
|
PtFin = &Charge::Fin3;
|
|
}
|
|
else if (nomtypeCharge == "TYPE4")
|
|
{ PtDebut = &Charge::Debut4;
|
|
PtAvance = &Charge::Avance4;
|
|
PtPrecedent = &Charge::Precedent4;
|
|
PtFin = &Charge::Fin4;
|
|
}
|
|
else if (nomtypeCharge == "TYPE5")
|
|
{ PtDebut = &Charge::Debut5;
|
|
PtAvance = &Charge::Avance5;
|
|
PtPrecedent = &Charge::Precedent5;
|
|
PtFin = &Charge::Fin5;
|
|
}
|
|
else if (nomtypeCharge == "TYPE6")
|
|
{ PtDebut = &Charge::Debut6;
|
|
PtAvance = &Charge::Avance6;
|
|
PtPrecedent = &Charge::Precedent6;
|
|
PtFin = &Charge::Fin6;
|
|
}
|
|
else if (nomtypeCharge == "TYPE7")
|
|
{ PtDebut = &Charge::Debut7;
|
|
PtAvance = &Charge::Avance7;
|
|
PtPrecedent = &Charge::Precedent7;
|
|
PtFin = &Charge::Fin7;
|
|
}
|
|
else
|
|
{ cout << "\n cas non prevu, ne devrait pas arriver, nomtypeCharge = ";
|
|
cout << nomtypeCharge << ", void Charge::Initialise(ParaAlgoControle& pa) " << endl;
|
|
Sortie (1);
|
|
}
|
|
// appel de la fonction ad hoc
|
|
coeff = 0.; // charge courante a priori, en fait si type3 elle vaut 1 tout le temps, même au début
|
|
(this->*PtDebut)();
|
|
|
|
// ==== verification des references ===
|
|
|
|
// --- cas des forces ponctuelles
|
|
// on parcours le tableau tabPonctuel
|
|
for (int i=1;i<= tabPonctuel.Taille();i++)
|
|
{ // recup de la reference correspondant au mot cle
|
|
const Reference& ref = lesRef->Trouve(tabPonctuel(i).NomRef(),tabPonctuel(i).NomMaillage());
|
|
const ReferenceNE & refN = ((ReferenceNE &) ref);
|
|
if (ref.Indic() != 1)
|
|
// cas où la référence ne correspond pas à des noeuds
|
|
{ cout << "\n ERREUR la reference: " << ref.Nom()
|
|
<< " ne correspond pas a des noeuds !!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces ponctuelles " << endl;
|
|
Sortie (1);
|
|
}
|
|
// dans le cas où il s'agit d'une ref relative à un champ, on vérifie que le nombre
|
|
// de valeurs enregistrées correspond bien au nombre de noeuds
|
|
if (tabPonctuel(i).Champ() != 0)
|
|
{ if (tabPonctuel(i).DimVect() != refN.Taille())
|
|
{ cout << "\n ERREUR la reference: " << ref.Nom()
|
|
<< " contiens "<<refN.Taille() << " noeuds "
|
|
<< " alors que le champ de valeurs lues stocke "
|
|
<< tabPonctuel(i).DimVect() << " vecteurs !!" ;
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces ponctuelles " << endl;
|
|
Sortie (1);
|
|
};
|
|
};
|
|
};
|
|
|
|
// --- cas des forces surfaciques uniformes
|
|
// on parcours le tableau tabFsurfac dans le repère absolu
|
|
for (int i=1;i<= tabFsurfac.Taille();i++)
|
|
{ // recup de la reference correspondant au mot cle
|
|
const Reference& refi = lesRef->Trouve(tabFsurfac(i).NomRef(),tabFsurfac(i).NomMaillage());
|
|
if ( ((refi.Indic() != 3)&& (ParaGlob::Dimension() == 3))
|
|
|| ((refi.Indic() != 3)&&(refi.Indic() != 4)&&(ParaGlob::Dimension() == 2))
|
|
|| (ParaGlob::Dimension() == 1))
|
|
// cas d'element
|
|
{ cout << "\n ERREUR la reference: " << refi.Nom()
|
|
<< " ne correspond pas a des frontieres d'element,"
|
|
<< " ce qui est necessaire pour les chargements surfacique !!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... " << endl;
|
|
Sortie (1);
|
|
}
|
|
const ReferenceAF & ref = ((ReferenceAF &) refi);
|
|
// Maintenant il faut vérifier que les surfaces en question sont possible pour l'élément
|
|
int nbref = ref.Taille();
|
|
for (int ns=1;ns<= nbref;ns++)
|
|
{ // on regarde si l'on est en 2D ou en 3D
|
|
bool surface_existe = false;
|
|
if (ParaGlob::Dimension() == 3)
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).SurfExiste(ref.NumeroFA(ns));
|
|
else if (ParaGlob::Dimension() == 2)
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).AreteExiste(ref.NumeroFA(ns));
|
|
if (!surface_existe)
|
|
{ cout << "\n ERREUR l'enregistrement " <<ns<< " de la reference: " << ref.Nom()
|
|
<< " correspondant à l'element :" << ref.NumeroElem(ns)
|
|
<< " n'accepte pas de numero de surface/arête : " << ref.NumeroFA(ns);
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces surfaciques uniformes " << endl;
|
|
Sortie (1);
|
|
}
|
|
};
|
|
// dans le cas où il s'agit d'une ref relative à un champ, on vérifie que le nombre
|
|
// de valeurs enregistrées correspond bien au nombre de noeuds
|
|
if (tabFsurfac(i).Champ() != 0)
|
|
{ if (tabFsurfac(i).DimVect() != ref.Taille())
|
|
{ cout << "\n ERREUR la reference: " << ref.Nom()
|
|
<< " contiens "<<ref.Taille() << " facette "
|
|
<< " alors que le champ de valeurs lues stocke "
|
|
<< tabFsurfac(i).DimVect() << " vecteurs !!" ;
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces surfaciques uniformes " << endl;
|
|
Sortie (1);
|
|
};
|
|
};
|
|
};
|
|
|
|
// --- cas des pression uniformes
|
|
// on parcours le tableau tabPresUnif
|
|
for (int i=1;i<= tabPresUnif.Taille();i++)
|
|
{ // recup de la reference correspondant au mot cle
|
|
const Reference& refi = lesRef->Trouve(tabPresUnif(i).NomRef(),tabPresUnif(i).NomMaillage());
|
|
if ( ( ((refi.Indic() != 3)&& (ParaGlob::Dimension() == 3)) // ce n'est pas une surface et on est en dim 3
|
|
// et ce n'est pas : une arête + dim 3 + axisymétrie
|
|
&& !((refi.Indic() == 4)&&((ParaGlob::Dimension() == 3)&&(ParaGlob::AxiSymetrie())))
|
|
)
|
|
|| ((refi.Indic() != 3)&&(refi.Indic() != 4)&&(ParaGlob::Dimension() == 2))
|
|
|| (ParaGlob::Dimension() == 1))
|
|
// cas d'element
|
|
{ cout << "\n ERREUR la reference: " << refi.Nom()
|
|
<< " ne correspond pas a des frontieres surfaciques d'element,"
|
|
<< " ce qui est necessaire pour les chargements de pression !!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... " << endl;
|
|
Sortie (1);
|
|
}
|
|
const ReferenceAF & ref = ((ReferenceAF &) refi);
|
|
// Maintenant il faut vérifier que les surfaces en question sont possible pour l'élément
|
|
int nbref = ref.Taille();
|
|
for (int ns=1;ns<= nbref;ns++)
|
|
{ // on regarde si l'on est en 2D ou 2D axisymétrique ou en 3D,
|
|
bool surface_existe = false;
|
|
if ( (ParaGlob::Dimension() == 2)
|
|
|| ((ParaGlob::Dimension() == 3)&&(ParaGlob::AxiSymetrie())))
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).AreteExiste(ref.NumeroFA(ns));
|
|
else if (ParaGlob::Dimension() == 3)
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).SurfExiste(ref.NumeroFA(ns));
|
|
if (!surface_existe)
|
|
{ cout << "\n ERREUR l'enregistrement " <<ns<< " de la reference: " << ref.Nom()
|
|
<< " correspondant à l'element :" << ref.NumeroElem(ns)
|
|
<< " n'accepte pas de numero de surface/arête : " << ref.NumeroFA(ns);
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des pressions uniformes " << endl;
|
|
Sortie (1);
|
|
};
|
|
};
|
|
// dans le cas où il s'agit d'une ref relative à un champ, on vérifie que le nombre
|
|
// de valeurs enregistrées correspond bien au nombre de noeuds
|
|
if (tabPresUnif(i).Champ() != 0)
|
|
{ if (tabPresUnif(i).DimVal() != ref.Taille())
|
|
{ cout << "\n ERREUR la reference: " << ref.Nom()
|
|
<< " contiens "<<ref.Taille() << " facette "
|
|
<< " alors que le champ de valeurs lues stocke "
|
|
<< tabPresUnif(i).DimVal() << " valeurs !!" ;
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des pressions uniformes " << endl;
|
|
Sortie (1);
|
|
};
|
|
};
|
|
};
|
|
|
|
// --- cas des pression unidirectionnelle
|
|
// on parcours le tableau PresUniDir dans le repère absolu
|
|
for (int i=1;i<= PresUniDir.Taille();i++)
|
|
{ // recup de la reference correspondant au mot cle
|
|
const Reference& refi = lesRef->Trouve(PresUniDir(i).NomRef(),PresUniDir(i).NomMaillage());
|
|
if ( ((refi.Indic() != 3)&& (ParaGlob::Dimension() == 3))
|
|
|| ((refi.Indic() != 3)&&(refi.Indic() != 4)&&(ParaGlob::Dimension() == 2))
|
|
|| (ParaGlob::Dimension() == 1))
|
|
// cas d'element
|
|
{ cout << "\n ERREUR la reference: " << refi.Nom()
|
|
<< " ne correspond pas a des frontieres surfaciques d'element,"
|
|
<< " ce qui est necessaire pour les chargements de pression unidirectionnelle!!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... " << endl;
|
|
Sortie (1);
|
|
}
|
|
const ReferenceAF & ref = ((ReferenceAF &) refi);
|
|
// a priori pour l'instant ce type de chargement n'est valide que pour la 3D, et plus
|
|
// non axisymétrique. On laisse cependant les tests pour les autres dimensions, au cas où
|
|
// plus-tard on change d'avis
|
|
if (ParaGlob::Dimension() !=3)
|
|
{ cout << "\n ERREUR: a priori le type de chargement suiveur en surfacique ne s'utilise que dans "
|
|
<< " un espace de travail 3D (classique), pour les autres dimensions il existe des chargements"
|
|
<< " equivalents specifiques : cf. la documentation ! ";
|
|
cout << "\n ==== reference du chargement: " << ref.Nom();
|
|
Sortie (1);
|
|
}
|
|
else if (ParaGlob::AxiSymetrie())
|
|
{ cout << "\n ERREUR: le type de chargement suiveur en surfacique ne s'utilise que dans "
|
|
<< " un espace de travail 3D (classique), en axisymetrique il existe des chargements"
|
|
<< " equivalents specifiques : cf. la documentation ! ";
|
|
cout << "\n ==== reference du chargement: " << ref.Nom();
|
|
Sortie (1);
|
|
};
|
|
// Maintenant il faut vérifier que les surfaces en question sont possible pour l'élément
|
|
int nbref = ref.Taille();
|
|
for (int ns=1;ns<= nbref;ns++)
|
|
{ // on regarde si l'on est en 2D ou en 3D
|
|
bool surface_existe = false;
|
|
if (ParaGlob::Dimension() == 3)
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).SurfExiste(ref.NumeroFA(ns));
|
|
else if (ParaGlob::Dimension() == 2)
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).AreteExiste(ref.NumeroFA(ns));
|
|
if (!surface_existe)
|
|
{ cout << "\n ERREUR l'enregistrement " <<ns<< " de la reference: " << ref.Nom()
|
|
<< " correspondant à l'element :" << ref.NumeroElem(ns)
|
|
<< " n'accepte pas de numero de surface/arête : " << ref.NumeroFA(ns);
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces forces qui varies localement avec la déformation " << endl;
|
|
Sortie (1);
|
|
};
|
|
// dans le cas où il s'agit d'une ref relative à un champ, on vérifie que le nombre
|
|
// de valeurs enregistrées correspond bien au nombre de noeuds
|
|
if (PresUniDir(i).Champ() != 0)
|
|
{ if (PresUniDir(i).DimVect() != ref.Taille())
|
|
{ cout << "\n ERREUR la reference: " << ref.Nom()
|
|
<< " contiens "<<ref.Taille() << " facette "
|
|
<< " alors que le champ de valeurs lues stocke "
|
|
<< PresUniDir(i).DimVect() << " vecteurs !!" ;
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des pression unidirectionnelle " << endl;
|
|
Sortie (1);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
// --- cas des pression hydrostatique
|
|
// on parcours le tableau PresHydro dans le repère absolu
|
|
for (int i=1;i<= PresHydro.Taille();i++)
|
|
{ // recup de la reference correspondant au mot cle
|
|
const Reference& refi = lesRef->Trouve(PresHydro(i).NomRef(),PresHydro(i).NomMaillage());
|
|
if ( ( ((refi.Indic() != 3)&& (ParaGlob::Dimension() == 3)) // ce n'est pas une surface et on est en dim 3
|
|
// et ce n'est pas : une arête + dim 3 + axisymétrie
|
|
&& !((refi.Indic() == 4)&&((ParaGlob::Dimension() == 3)&&(ParaGlob::AxiSymetrie())))
|
|
)
|
|
|| ((refi.Indic() != 3)&&(refi.Indic() != 4)&&(ParaGlob::Dimension() == 2))
|
|
|| (ParaGlob::Dimension() == 1))
|
|
// cas d'element
|
|
{ cout << "\n ERREUR la reference: " << refi.Nom()
|
|
<< " ne correspond pas a des frontieres surfaciques d'element,"
|
|
<< " ce qui est necessaire pour les chargements surfacique hydrostatique!!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... " << endl;
|
|
Sortie (1);
|
|
};
|
|
const ReferenceAF & ref = ((ReferenceAF &) refi);
|
|
// Maintenant il faut vérifier que les surfaces en question sont possible pour l'élément
|
|
int nbref = ref.Taille();
|
|
for (int ns=1;ns<= nbref;ns++)
|
|
{ // on regarde si l'on est en 2D ou en 3D
|
|
bool surface_existe = false;
|
|
if ( (ParaGlob::Dimension() == 2)
|
|
|| ((ParaGlob::Dimension() == 3)&&(ParaGlob::AxiSymetrie())))
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).AreteExiste(ref.NumeroFA(ns));
|
|
else if (ParaGlob::Dimension() == 3)
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).SurfExiste(ref.NumeroFA(ns));
|
|
if (!surface_existe)
|
|
{ cout << "\n ERREUR l'enregistrement " <<ns<< " de la reference: " << ref.Nom()
|
|
<< " correspondant à l'element :" << ref.NumeroElem(ns)
|
|
<< " n'accepte pas de numero de surface/arete : " << ref.NumeroFA(ns);
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas de la pression hydrostatique " << endl;
|
|
Sortie (1);
|
|
};
|
|
};
|
|
};
|
|
|
|
// --- cas des pression hydrodynamique
|
|
// on parcours le tableau coefHydroDyna dans le repère absolu
|
|
for (int i=1;i<= coefHydroDyna.Taille();i++)
|
|
{ // recup de la reference correspondant au mot cle
|
|
const Reference& refi = lesRef->Trouve(coefHydroDyna(i).NomRef(),coefHydroDyna(i).NomMaillage());
|
|
if ( ((refi.Indic() != 3)&& (ParaGlob::Dimension() == 3))
|
|
|| ((refi.Indic() != 3)&&(refi.Indic() != 4)&&(ParaGlob::Dimension() == 2))
|
|
|| (ParaGlob::Dimension() == 1))
|
|
// cas d'element
|
|
{ cout << "\n ERREUR la reference: " << refi.Nom()
|
|
<< " ne correspond pas a des frontieres surfaciques d'element,"
|
|
<< " ce qui est necessaire pour les chargements hydrosdynamiques !!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... " << endl;
|
|
Sortie (1);
|
|
}
|
|
const ReferenceAF & ref = ((ReferenceAF &) refi);
|
|
// Maintenant il faut vérifier que les surfaces en question sont possible pour l'élément
|
|
int nbref = ref.Taille();
|
|
for (int ns=1;ns<= nbref;ns++)
|
|
{ // on regarde si l'on est en 2D ou en 3D
|
|
bool surface_existe = false;
|
|
if (ParaGlob::Dimension() == 3)
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).SurfExiste(ref.NumeroFA(ns));
|
|
else if (ParaGlob::Dimension() == 2)
|
|
surface_existe =
|
|
(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).AreteExiste(ref.NumeroFA(ns));
|
|
if (!surface_existe)
|
|
{ cout << "\n ERREUR l'enregistrement " <<ns<< " de la reference: " << ref.Nom()
|
|
<< " correspondant à l'element :" << ref.NumeroElem(ns)
|
|
<< " n'accepte pas de numero de surface/arete : " << ref.NumeroFA(ns);
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas de la pression hydrodynamique " << endl;
|
|
Sortie (1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- cas des forces linéqiue uniformes
|
|
// on parcours le tableau tabFlineique dans le repère absolu
|
|
for (int i=1;i<= tabFlineique.Taille();i++)
|
|
{ // recup de la reference correspondant au mot cle
|
|
const Reference& refi = lesRef->Trouve(tabFlineique(i).NomRef(),tabFlineique(i).NomMaillage());
|
|
if (refi.Indic() != 4)
|
|
// cas d'arrêtes
|
|
{ cout << "\n ERREUR la reference: " << refi.Nom()
|
|
<< " ne correspond pas a des frontieres d'arête d'element,"
|
|
<< " ce qui est necessaire pour les chargements lineique !!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... " << endl;
|
|
Sortie (1);
|
|
};
|
|
const ReferenceAF & ref = ((ReferenceAF &) refi);
|
|
// Maintenant il faut vérifier que les aretes en question sont possible pour l'élément
|
|
int nbref = ref.Taille();
|
|
for (int ns=1;ns<= nbref;ns++)
|
|
if (!(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).AreteExiste(ref.NumeroFA(ns)))
|
|
{ cout << "\n ERREUR l'enregistrement " <<ns<< " de la reference: " << ref.Nom()
|
|
<< " correspondant à l'element :" << ref.NumeroElem(ns)
|
|
<< " n'accepte pas de numero d'arete : " << ref.NumeroFA(ns);
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces linéiques uniformes " << endl;
|
|
Sortie (1);
|
|
};
|
|
// dans le cas où il s'agit d'une ref relative à un champ, on vérifie que le nombre
|
|
// de valeurs enregistrées correspond bien au nombre d'éléments
|
|
if (tabFlineique(i).Champ() != 0)
|
|
{ if (tabFlineique(i).DimVect() != ref.Taille())
|
|
{ cout << "\n ERREUR la reference: " << ref.Nom()
|
|
<< " contiens "<<ref.Taille() << " aretes "
|
|
<< " alors que le champ de valeurs lues stocke "
|
|
<< tabFlineique(i).DimVect() << " vecteurs !!" ;
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces linéqiue uniformes " << endl;
|
|
Sortie (1);
|
|
};
|
|
};
|
|
};
|
|
|
|
// --- cas des forces linéiques suiveuses uniformes
|
|
// tout d'abord on vérifie que l'on est en 2D ou 1D si ces forces existent ou 3D axi
|
|
int tabFlineiqueSuiv_Taille = tabFlineiqueSuiv.Taille();
|
|
if ((tabFlineiqueSuiv_Taille > 0)&&(ParaGlob::Dimension() != 2)&&(!ParaGlob::AxiSymetrie()))
|
|
{ cout << "\n ERREUR la dimension est différente de 2 et il y a des forces linéiques suiveuses défini"
|
|
<< " \n ceci n'est pas possible !! (indétermination de direction)"
|
|
<< " \n Charge::Initialise(LesMaillages * lesMail,etc ... " << endl;
|
|
Sortie (1);
|
|
}
|
|
|
|
// on parcours le tableau tabFlineiqueSuiv dans le repère global
|
|
for (int i=1;i<= tabFlineiqueSuiv.Taille();i++)
|
|
{ // recup de la reference correspondant au mot cle
|
|
const Reference& refi = lesRef->Trouve(tabFlineiqueSuiv(i).NomRef(),tabFlineiqueSuiv(i).NomMaillage());
|
|
if (refi.Indic() != 4)
|
|
// cas d'arrêtes
|
|
{ cout << "\n ERREUR la reference: " << refi.Nom()
|
|
<< " ne correspond pas a des frontieres d'arête d'element,"
|
|
<< " ce qui est necessaire pour les chargements lineique !!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... " << endl;
|
|
Sortie (1);
|
|
}
|
|
const ReferenceAF & ref = ((ReferenceAF &) refi);
|
|
// Maintenant il faut vérifier que les aretes en question sont possible pour l'élément
|
|
int nbref = ref.Taille();
|
|
for (int ns=1;ns<= nbref;ns++)
|
|
if (!(lesmail->Element_LesMaille(ref.Nbmaille(), ref.NumeroElem(ns))).AreteExiste(ref.NumeroFA(ns)))
|
|
{ cout << "\n ERREUR l'enregistrement " <<ns<< " de la reference: " << ref.Nom()
|
|
<< " correspondant à l'element :" << ref.NumeroElem(ns)
|
|
<< " n'accepte pas de numero d'arete : " << ref.NumeroFA(ns);
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces linéiques suiveuses uniformes " << endl;
|
|
Sortie (1);
|
|
};
|
|
// dans le cas où il s'agit d'une ref relative à un champ, on vérifie que le nombre
|
|
// de valeurs enregistrées correspond bien au nombre d'éléments
|
|
if (tabFlineiqueSuiv(i).Champ() != 0)
|
|
{ if (tabFlineiqueSuiv(i).DimVect() != ref.Taille())
|
|
{ cout << "\n ERREUR la reference: " << ref.Nom()
|
|
<< " contiens "<<ref.Taille() << " aretes "
|
|
<< " alors que le champ de valeurs lues stocke "
|
|
<< tabFlineiqueSuiv(i).DimVect() << " vecteurs !!" ;
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces linéiques suiveuses uniformes " << endl;
|
|
Sortie (1);
|
|
};
|
|
};
|
|
};
|
|
|
|
// --- cas des forces volumiques
|
|
// on parcours le tableau tabFvol dans le repère absolu
|
|
for (int i=1;i<= tabFvol.Taille();i++)
|
|
{ // recup de la reference correspondant au mot cle
|
|
const Reference& refi = lesRef->Trouve(tabFvol(i).NomRef(),tabFvol(i).NomMaillage());
|
|
if (refi.Indic() != 2)
|
|
// cas d'elements
|
|
{ cout << "\n ERREUR la reference: " << refi.Nom()
|
|
<< " ne correspond pas a des element,"
|
|
<< " ce qui est necessaire pour les chargements volumique !!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces volumiques uniformes " << endl;
|
|
Sortie (1);
|
|
};
|
|
// dans le cas où il s'agit d'une ref relative à un champ, on vérifie que le nombre
|
|
// de valeurs enregistrées correspond bien au nombre d'éléments
|
|
const ReferenceNE & refE = ((ReferenceNE &) refi);
|
|
if (tabFvol(i).Champ() != 0)
|
|
{ if (tabFvol(i).DimVect() != refE.Taille())
|
|
{ cout << "\n ERREUR la reference: " << refE.Nom()
|
|
<< " contiens "<<refE.Taille() << " elements "
|
|
<< " alors que le champ de valeurs lues stocke "
|
|
<< tabFvol(i).DimVect() << " vecteurs !!" ;
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des forces volumiques " << endl;
|
|
Sortie (1);
|
|
};
|
|
};
|
|
};
|
|
// --- cas des torseurs de forces ponctuelles
|
|
// on parcours le tableau tabTorseurPonct et on renseigne les torseurs pour les prochains calculs
|
|
for (int i=1;i<= tabTorseurPonct.Taille();i++)
|
|
{ { // recup de la reference correspondant au mot cle
|
|
const Reference& ref = lesRef->Trouve(tabTorseurPonct(i).NomRef(),tabTorseurPonct(i).NomMaillage());
|
|
//const ReferenceNE & refN = ((ReferenceNE &) ref);
|
|
if (ref.Indic() != 1)
|
|
// cas où la référence ne correspond pas à des noeuds
|
|
{ cout << "\n ERREUR la reference: " << ref.Nom()
|
|
<< " ne correspond pas a des noeuds !!";
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des torseurs de forces ponctuelles " << endl;
|
|
Sortie (1);
|
|
};
|
|
}
|
|
// on met à jour les fonctions nD internes si nécessaire
|
|
if (!tabTorseurPonct(i).Mise_en_place_des_fonction_nD(lesFonctionsnD))
|
|
{ cout << "\n ERREUR d'association de fonction nD: pour la condition " << tabTorseurPonct(i)
|
|
<< " revoir la mise en donnees !!";
|
|
if (ParaGlob::param->NiveauImpression() > 3)
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des torseurs de forces ponctuelles " << endl;
|
|
Sortie (1);
|
|
};
|
|
{ //On s'occupe maintenant des noeuds éventuellement à rattacher
|
|
// récup du nom de la ref éventuelle de noeuds pour le centre du moment
|
|
Deux_String nom_ref = tabTorseurPonct(i).Nom_Ref_pour_Ce();
|
|
if ( nom_ref.nom2.length())
|
|
{// cas du centre de gravité d'une référence de noeuds
|
|
int num_mail = 1; // init par défaut
|
|
if ( nom_ref.nom1.length()) // s'il y a un nom de maillage
|
|
lesmail->NumMaillage(nom_ref.nom2); // on en récupère le numéro
|
|
const Reference& ref =
|
|
((Reference &) lesRef->Trouve(nom_ref.nom2,num_mail));
|
|
if (ref.Indic() != 1)
|
|
// cas où la référence ne correspond pas à des noeuds
|
|
{ cout << "\n ERREUR la reference: " << ref.Nom()
|
|
<< " prevu pour un centre de gravite pour le calcul de torseur d'action, "
|
|
<< " ne correspond pas a des noeuds !!";
|
|
if (ParaGlob::param->NiveauImpression() > 3)
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des torseurs de forces ponctuelles " << endl;
|
|
Sortie (1);
|
|
};
|
|
const ReferenceNE & refN = ((ReferenceNE &) ref);
|
|
// on va constituer une liste de noeuds que l'on va transmettre au torseur
|
|
list < const Noeud * > liNoe;
|
|
int reftaille = refN.Taille();
|
|
for (int nn =1; nn<= reftaille;nn++) // nn = position des num de noeud
|
|
liNoe.push_back(&(lesmail->Noeud_LesMaille(refN.Nbmaille(),refN.Numero(nn))));
|
|
// mise en place des infos pour le centre si celui-ci dépend des noeuds
|
|
if (!tabTorseurPonct(i).Def_tab_noeud_pour_centre(liNoe))
|
|
{ cout << "\n ERREUR dans la definition du centre de ref du momemt pour la condition " << tabTorseurPonct(i)
|
|
<< " revoir la mise en donnees !!";
|
|
if (ParaGlob::param->NiveauImpression() > 3)
|
|
cout << " Charge::Initialise(LesMaillages * lesMail,etc ... "
|
|
<< "\n cas des torseurs de forces ponctuelles " << endl;
|
|
Sortie (1);
|
|
};
|
|
}
|
|
};
|
|
{//On définit des places de stockage intermédiaire pour optimiser les temps de calcul
|
|
BlocCharge< BlocDdlLim<PTorseurPonct> >& tabTorseurPonct_i = tabTorseurPonct(i);
|
|
const ReferenceNE & ref =
|
|
((ReferenceNE &) lesRef->Trouve(tabTorseurPonct_i.NomRef(),tabTorseurPonct_i.NomMaillage()));
|
|
int reftaille = ref.Taille();
|
|
tab_P(i).Change_taille(reftaille);
|
|
t_force(i).Change_taille(reftaille);
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
//----- lecture écriture de base info -----
|
|
// lecture base info
|
|
// = 1 : on récupère tout
|
|
// = 2 : on récupère uniquement les données variables (supposées comme telles)
|
|
void Charge::Lecture_base_info(ifstream& entr,const int cas,LesReferences& ,LesCourbes1D& lesCourbes1D
|
|
,LesFonctions_nD& lesFonctionsnD)
|
|
{switch (cas)
|
|
{ case 1 : // ------- on récupère tout -------------------------
|
|
|
|
{ cout << "== lecture du chargement \n";
|
|
string toto;entr >> toto ; // lecture en tête
|
|
string type; entr >> type; // lecture du premier type
|
|
while (type != "type_de_charge")
|
|
{ if (type == "force_volumique")
|
|
// cas des densites de force volumique dans le repère absolu
|
|
entr >> tabFvol;
|
|
if (type == "force_surfacique")
|
|
// cas des densites de force surfacique dans le repère absolu
|
|
entr >> tabFsurfac;
|
|
if (type == "force_pression_uniforme")
|
|
// cas des pressions uniformement repartis, appliquee normalement a la surface
|
|
entr >> tabPresUnif;
|
|
if (type == "forces_ponctuelles")
|
|
// cas des forces ponctuelles sur un noeud
|
|
entr >> tabPonctuel;
|
|
if (type == "pression_unidirectionnelle")
|
|
// pression unidirectionnelle type pression dynamique des fluides
|
|
entr >> PresUniDir;
|
|
if (type == "pression_hydrostatique")
|
|
// pression hydrostatique
|
|
entr >> PresHydro;
|
|
if (type == "pression_hydrodynamique")
|
|
// pression hydrodynamique
|
|
entr >> coefHydroDyna;
|
|
if (type == "forces_lineiques")
|
|
// densite de force lineique dans le repère absolu
|
|
entr >> tabFlineique;
|
|
if (type == "forces_lineiques_suiveuses")
|
|
// densite de force lineique suiveuse
|
|
entr >> tabFlineiqueSuiv;
|
|
if (type == "torseurs_forces_ponctuelles" )
|
|
// densite de torseurs de forces ponctuelles
|
|
entr >> tabTorseurPonct;
|
|
entr >> type;
|
|
}
|
|
// type d'application du chargement
|
|
entr >> nomtypeCharge;
|
|
int tabType_taille;
|
|
entr >> toto >> tabType_taille;
|
|
tabType.Change_taille(tabType_taille);
|
|
for (int i=1;i<= tabType_taille;i++)
|
|
entr >> tabType(i);
|
|
// cas particulier d'une fonction de charge
|
|
if ((nomtypeCharge == "TYPE4") || (nomtypeCharge == "TYPE5"))
|
|
{ string nom; entr >> nom;
|
|
if (nom != " fonction_de_charge: ")
|
|
{ cout << "\n erreur en lecture de la fonction de charge, "
|
|
<< " on attendait fonction_de_charge: et on a lue " << nom
|
|
<< "\n Charge::Lecture_base_info(...";
|
|
Sortie(1);
|
|
};
|
|
f_charge = lesCourbes1D.Lecture_pour_base_info(entr,cas,f_charge);
|
|
{if (f_charge->NomCourbe() == "_") interne_f_charge=true; else interne_f_charge=false;};
|
|
};
|
|
// on lit éventuellement les paramètres particuliers de gestion des types de chargement
|
|
if (nomtypeCharge == "TYPE5")
|
|
entr >> toto >> num_pt_courant_type5;
|
|
// on écrit le type de gestion du temps fin
|
|
entr >> toto >> temps_fin_non_stricte;
|
|
break;
|
|
}
|
|
case 2 : // ----------- lecture uniquement de se qui varie --------------------
|
|
{
|
|
// le parametre global d'avancement de la charge
|
|
string toto;
|
|
entr >> toto; // lecture de l'entête
|
|
// compteur_essai_increment d'increment
|
|
entr >> toto >> compteur_essai_increment;
|
|
// multi = l'increment courant de multiplication de la charge
|
|
// coeff = niveau actuel de la charge
|
|
entr >> toto >> multi >> coeff;
|
|
// on lit également le numéro du type pour la gestion éventuelle de reprise
|
|
// avec des types différents
|
|
short int numtype; entr >> numtype;
|
|
if ((numtype != 5) && (nomtypeCharge == "TYPE5"))
|
|
{ // cas d'une reprise avec le type 5, à la suite d'un autre type -> a priori impossible
|
|
cout << "\n erreur en restart !! on ne peut pas redemarrer avec le type 5 de chargement "
|
|
<< " a la suite d'un autre type !! ";
|
|
if (ParaGlob::NiveauImpression() >= 4){ cout << "\n Charge::Lecture_base_info(.... ";};
|
|
Sortie(1);
|
|
};
|
|
// on lit éventuellement les paramètres particuliers de gestion des types de chargement
|
|
if (nomtypeCharge == "TYPE5")
|
|
entr >> toto >> num_pt_courant_type5;
|
|
// temps cpu
|
|
entr >> toto >> temps_cpu_chargement;
|
|
break;
|
|
}
|
|
default :
|
|
{ cout << "\nErreur : valeur incorrecte du type de sauvegarde !\n";
|
|
cout << "Charge::Lecture_base_info(ifstream& entr,const int cas)"
|
|
<< " cas= " << cas << endl;
|
|
Sortie(1);
|
|
}
|
|
};
|
|
};
|
|
|
|
// écriture base info
|
|
// = 1 : on sauvegarde tout
|
|
// = 2 : on sauvegarde uniquement les données variables (supposées comme telles)
|
|
void Charge::Ecriture_base_info(ofstream& sort,const int cas)
|
|
{switch (cas)
|
|
{ case 1 : // ------- on sauvegarde tout -------------------------
|
|
{ sort << "\n ****chargement \n" ;
|
|
// cas des densites de force volumique dans le repère absolu
|
|
if (tabFvol.Taille() != 0)
|
|
sort << " force_volumique " << tabFvol;
|
|
// cas des densites de force surfacique dans le repère absolu
|
|
if (tabFsurfac.Taille() != 0)
|
|
sort << " force_surfacique " << tabFsurfac;
|
|
// cas des pressions uniformement repartis, appliquee normalement a la surface
|
|
if (tabPresUnif.Taille() != 0)
|
|
sort << " force_pression_uniforme " << tabPresUnif;
|
|
// cas des forces ponctuelles sur un noeud
|
|
if (tabPonctuel.Taille() != 0)
|
|
sort << " forces_ponctuelles " << tabPonctuel;
|
|
// pression unidirectionnelle type pression dynamique des fluides
|
|
if (PresUniDir.Taille() != 0)
|
|
sort << " pression_unidirectionnelle " << PresUniDir;
|
|
// pression hydrostatique
|
|
if (PresHydro.Taille() != 0)
|
|
sort << " pression_hydrostatique " << PresHydro;
|
|
// pression hydrodynamique
|
|
if (coefHydroDyna.Taille() != 0)
|
|
sort << " pression_hydrodynamique " << coefHydroDyna;
|
|
// densite de force lineique dans le repère absolu
|
|
if (tabFlineique.Taille() != 0)
|
|
sort << " forces_lineiques " << tabFlineique;
|
|
// densite de force lineique suiveuses
|
|
if (tabFlineiqueSuiv.Taille() != 0)
|
|
sort << " forces_lineiques_suiveuses " << tabFlineiqueSuiv;
|
|
// densite de torseurs de forces ponctuelles
|
|
if (tabTorseurPonct.Taille() != 0)
|
|
sort << " torseurs_forces_ponctuelles " << tabTorseurPonct;
|
|
// type d'application du chargement
|
|
sort << " type_de_charge " << nomtypeCharge;
|
|
int tabType_taille = tabType.Taille();
|
|
sort << " parametres " << tabType_taille;
|
|
for (int i=1;i<= tabType_taille;i++)
|
|
sort << " " << tabType(i);
|
|
// cas particulier d'une fonction de charge
|
|
if ((nomtypeCharge == "TYPE4") || (nomtypeCharge == "TYPE5"))
|
|
{ sort << " fonction_de_charge: " ;
|
|
LesCourbes1D::Ecriture_pour_base_info(sort,cas,f_charge);
|
|
}
|
|
// on écrit également les paramètres particuliers de gestion des types de chargement
|
|
if (nomtypeCharge == "TYPE5")
|
|
sort << " para_type5 " << num_pt_courant_type5 << " ";
|
|
// on écrit le type de gestion du temps fin
|
|
sort << "\n gestion_temps_fin " << temps_fin_non_stricte;
|
|
break;
|
|
}
|
|
case 2 : // ----------- sauvegarde uniquement de se qui varie --------------------
|
|
{
|
|
// le parametre global d'avancement de la charge
|
|
// sort << "\n temps " << temps << " ";
|
|
// compteur_essai_increment d'increment
|
|
sort << "\n ****chargement " ;
|
|
sort <<"\n compteur_essai_increment " << compteur_essai_increment << " ";
|
|
// multi = l'increment courant de multiplication de la charge
|
|
// coeff = niveau actuel de la charge
|
|
sort << "\n multi_et_coeff " << multi << " " << coeff << " ";
|
|
// on sort également le numéro du type pour la gestion éventuelle de reprise
|
|
// avec des types différents
|
|
sort << nomtypeCharge.substr(4,nomtypeCharge.length()-4) << " ";
|
|
if (nomtypeCharge == "TYPE5")
|
|
{sort << " para_type5 " << num_pt_courant_type5 << " ";};
|
|
// else { sort << "---" << 0 ; }; // de manière à ne pas avoir de pb si on change
|
|
// temps cpu
|
|
sort << "\n temps_cpu_charge: "<< temps_cpu_chargement;
|
|
break;
|
|
}
|
|
default :
|
|
{ cout << "\nErreur : valeur incorrecte du type de sauvegarde !\n";
|
|
cout << "Charge::Ecriture_base_info(ofstream& sort,const int cas)"
|
|
<< " cas= " << cas << endl;
|
|
Sortie(1);
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|