Skip to content
Snippets Groups Projects
Commit 0701f73c authored by Maxime Graulich's avatar Maxime Graulich
Browse files

Add Android project

parent a2e508fa
No related branches found
No related tags found
No related merge requests found
Showing
with 2666 additions and 0 deletions
// Permanent magnet synchronous machine
// Example of Prof. Dr. Mauricio Valencia Ferreira da Luz (Florianopolis, August 23, 2010)
// Modified and customised for Onelab by Ruth V. Sabariego (February, 2013)
mm = 1e-3 ;
deg2rad = Pi/180 ;
DefineConstant[ NbrPoles = { 1, Choices {1="1",
2="2",
4="4",
8="8"},
Label "Number of poles in FE model",
Path "Input/1", Highlight "Blue", Visible 1} ] ;
DefineConstant[ InitialRotorAngle_deg = {7.5, Label "Start rotor angle", Path "Input/20", Highlight "AliceBlue"} ];
//--------------------------------------------------------------------------------
InitialRotorAngle = InitialRotorAngle_deg*deg2rad ; // initial rotor angle, 0 if aligned
AxialLength = 35*mm ;
//------------------------------------------------
//------------------------------------------------
NbrPolesTot = 8 ; // number of poles in complete cross-section
SymmetryFactor = NbrPolesTot/NbrPoles ;
Flag_Symmetry = (SymmetryFactor==1)?0:1 ;
NbrSectTot = NbrPolesTot ; // number of "rotor teeth"
NbrSect = NbrSectTot*NbrPoles/NbrPolesTot ; // number of "rotor teeth" in FE model
//--------------------------------------------------------------------------------
//------------------------------------------------
// Stator
//------------------------------------------------
NbrSectTotStator = 24; // number of stator teeth
NbrSectStator = NbrSectTotStator*NbrPoles/NbrPolesTot; // number of stator teeth in FE model
//--------------------------------------------------------------------------------
lm = 2.352*mm ; // magnet height
Th_magnet = 32.67 *deg2rad ; // angle in degrees 0 < Th_magnet < 45
//--------------------------------------------------------------------------------
rRext = 25.6*mm;
rR1 = 10.5*mm;
rR2 = (rRext-lm); //23.243e-03;
rR3 = (rRext-0.7389*lm); //23.862e-03;
rR4 = (rRext-0.72278*lm); //23.9e-03;
rR5 = rRext; //25.6e-03;
rS1 = 26.02*mm;
rS2 = 26.62*mm;
rS3 = 26.96*mm;
rS4 = 38.16*mm;
rS5 = 38.27*mm;
rS6 = 40.02*mm;
rS7 = 46.00*mm;
Gap = rS1-rR5;
rB1 = rR5+Gap/3;
rB1b = rB1;
rB2 = rR5+Gap*2/3;
A0 = 45 * deg2rad ; // with this choice, axis A of stator is at 30 degrees with regard to horizontal axis
A1 = 0 * deg2rad ; // Rotor initial aligned position, current position in angRot
// ----------------------------------------------------
// Numbers for physical regions in .geo and .pro files
// ----------------------------------------------------
// Rotor
ROTOR_FE = 1000 ;
ROTOR_AIR = 1001 ;
ROTOR_AIRGAP = 1002 ;
ROTOR_MAGNET = 1010 ; // Index for first Magnet (1/8 model->1; full model->8)
ROTOR_BND_MOVING_BAND = 1100 ; // Index for first line (1/8 model->1; full model->8)
ROTOR_BND_A0 = 1200 ;
ROTOR_BND_A1 = 1201 ;
SURF_INT = 1202 ;
// Stator
STATOR_FE = 2000 ;
STATOR_AIR = 2001 ;
STATOR_AIRGAP = 2002 ;
STATOR_BND_MOVING_BAND = 2100 ;// Index for first line (1/8 model->1; full model->8)
STATOR_BND_A0 = 2200 ;
STATOR_BND_A1 = 2201 ;
STATOR_IND = 2300 ; //Index for first Ind (1/8 model->3; full model->24)
STATOR_IND_AP = STATOR_IND + 1 ; STATOR_IND_BM = STATOR_IND + 2 ;STATOR_IND_CP = STATOR_IND + 3 ;
STATOR_IND_AM = STATOR_IND + 4 ; STATOR_IND_BP = STATOR_IND + 5 ;STATOR_IND_CM = STATOR_IND + 6 ;
SURF_EXT = 3000 ; // outer boundary
MOVING_BAND = 9999 ;
NICEPOS = 111111 ;
Include "pmsm_data.geo";
Mesh.Algorithm = 1;
Geometry.CopyMeshingMethod = 1;
Mesh.CharacteristicLengthFactor = 1.5 ;
// Mesh characteristic lengths
s = 0.4 ;
pR1=(rR2-rR1)/6.*s;
pR2=(rR2-rR1)/6.*s;
pS1=(rS7-rS1)/7.*s;
pS2=(rS7-rS1)/12.*s;
pS3=(rS6-rS3)/10.*s;
NbrDivMB = 2*Ceil[2*Pi*rRext/8/pR1]; //1/8 Moving band
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
cen = newp ; Point(cen)={0,0,0,pR1};
nicepos_rotor[] = {};
nicepos_stator[] = {};
Include "pmsm_rotor.geo";
Include "pmsm_stator.geo";
// For nice visualisation...
Mesh.Light = 0 ;
//Mesh.SurfaceFaces = 1; Mesh.SurfaceEdges=0;
Hide { Point{ Point '*' }; }
Hide { Line{ Line '*' }; }
Show { Line{ nicepos_rotor[], nicepos_stator[] }; }
Physical Line(NICEPOS) = { nicepos_rotor[], nicepos_stator[] };
//For post-processing...
View[0].Light = 0;
View[0].NbIso = 25; // Number of intervals
View[0].IntervalsType = 1;
DefineConstant[ Flag_AddInfo = {0, Choices{0,1},
Label "Add info about phases and axis",
Path "Input/1"} ];
For i In {PostProcessing.NbViews-1 : 0 : -1}
If(StrFind(View[i].Attributes, "tmp"))
Delete View[i];
EndIf
EndFor
If(Flag_AddInfo)
rr = 1.25 * rS3 ;
For k In {0:NbrPoles-1}
xa[] += rr*Cos(1*Pi/24+k*Pi/4) ; ya[] += rr*Sin(1*Pi/24+k*Pi/4) ;
xb[] += rr*Cos(3*Pi/24+k*Pi/4) ; yb[] += rr*Sin(3*Pi/24+k*Pi/4) ;
xc[] += rr*Cos(5*Pi/24+k*Pi/4) ; yc[] += rr*Sin(5*Pi/24+k*Pi/4) ;
EndFor
// Adding some axes
rr0 = 0.3 * rS7 ;
rr1 = 1.3 * rS7 ;
th_d = InitialRotorAngle ;
th_q = th_d + 22.5 * deg2rad ;
th_a = 30 * deg2rad ;
th_b = (30 + 120/4) * deg2rad ;
th_c = (30 + 240/4) * deg2rad ;
ff = 0.9;
xd[0] = rr0*Cos(th_d) ; yd[0] = rr0*Sin(th_d) ;
xd[1] = ff*rr1*Cos(th_d) ; yd[1] = ff*rr1*Sin(th_d) ;
xq[0] = rr0*Cos(th_q) ; yq[0] = rr0*Sin(th_q) ;
xq[1] = ff*rr1*Cos(th_q) ; yq[1] = ff*rr1*Sin(th_q) ;
xaa[0] = rr0*Cos(th_a) ; yaa[0] = rr0*Sin(th_a) ;
xaa[1] = rr1*Cos(th_a) ; yaa[1] = rr1*Sin(th_a) ;
xbb[0] = rr0*Cos(th_b) ; ybb[0] = rr0*Sin(th_b) ;
xbb[1] = rr1*Cos(th_b) ; ybb[1] = rr1*Sin(th_b) ;
xcc[0] = rr0*Cos(th_c) ; ycc[0] = rr0*Sin(th_c) ;
xcc[1] = rr1*Cos(th_c) ; ycc[1] = rr1*Sin(th_c) ;
Include "info_view.geo";
EndIf
//
// Permanent Magnet Synchronous Generator
//
Include "pmsm_data.geo";
DefineConstant[ Flag_NL = {0,
Choices{ 0="Linear",
1="Nonlinear BH curve"},
Label "Fe magnetic law",
Path "Input/3", Highlight "Blue"} ] ;
DefineConstant[ Flag_SrcType_Stator = {0,
Choices{ 0="None",
1="Current" },
Label "Source Type in Stator",
Path "Input/4", Highlight "Blue", Visible 1} ] ;
DefineConstant[ Flag_SrcType_Rotor = {0,
Choices{ 0="None",
1="Current" },
Label "Source Type in Rotor",
Path "Input/5", Highlight "Blue", Visible 0} ] ;
DefineConstant[ Flag_Cir = {!Flag_SrcType_Stator, Choices{0,1},
Label "Use circuit in Stator", ReadOnly 1, Visible 0} ] ;
Group {
Stator_Fe = #STATOR_FE ;
Stator_Al = #{};
Stator_Cu = #{};
Stator_Air = #STATOR_AIR ;
Stator_Airgap = #STATOR_AIRGAP ;
Stator_Bnd_A0 = #STATOR_BND_A0 ;
Stator_Bnd_A1 = #STATOR_BND_A1 ;
Rotor_Fe = #ROTOR_FE ;
Rotor_Al = #{};
Rotor_Cu = #{};
Stator_Air = #STATOR_AIR ;
Stator_Airgap = #STATOR_AIRGAP ;
Stator_Bnd_MB = #STATOR_BND_MOVING_BAND ;
Stator_Bnd_A0 = #STATOR_BND_A0 ;
Stator_Bnd_A1 = #STATOR_BND_A1 ;
Rotor_Fe = #ROTOR_FE ;
Rotor_Air = #ROTOR_AIR ;
Rotor_Airgap = #ROTOR_AIRGAP ;
Rotor_Bnd_A0 = #ROTOR_BND_A0 ;
Rotor_Bnd_A1 = #ROTOR_BND_A1 ;
MovingBand_PhysicalNb = #MOVING_BAND ; // Fictitious number for moving band, not in the geo file
Surf_Inf = #SURF_EXT ;
Surf_bn0 = #SURF_INT ;
Surf_cutA0 = #{STATOR_BND_A0, ROTOR_BND_A0};
Surf_cutA1 = #{STATOR_BND_A1, ROTOR_BND_A1};
Dummy = #NICEPOS;
nbMagnets = NbrPolesTot/SymmetryFactor ;
For k In {1:nbMagnets}
Rotor_Magnet~{k} = Region[ (ROTOR_MAGNET+k-1) ];
Rotor_Magnets += Region[ Rotor_Magnet~{k} ];
EndFor
nbInds = (Flag_Symmetry) ? NbrPoles*NbrSectTotStator/NbrPolesTot : NbrSectTotStator ;
Printf("NbrPoles=%g, nbInds=%g SymmetryFactor=%g", NbrPoles, nbInds, SymmetryFactor);
Stator_Ind_Ap = #{}; Stator_Ind_Am = #{STATOR_IND_AM};
Stator_Ind_Bp = #{}; Stator_Ind_Bm = #{STATOR_IND_BM};
Stator_Ind_Cp = #{STATOR_IND_CP}; Stator_Ind_Cm = #{};
If(NbrPoles > 1)
Stator_Ind_Ap += #STATOR_IND_AP;
Stator_Ind_Bp += #STATOR_IND_BP;
Stator_Ind_Cm += #STATOR_IND_CM;
EndIf
PhaseA = Region[{ Stator_Ind_Ap, Stator_Ind_Am }];
PhaseB = Region[{ Stator_Ind_Bp, Stator_Ind_Bm }];
PhaseC = Region[{ Stator_Ind_Cp, Stator_Ind_Cm }];
// Provisional: Just one physical region for nice graph in Onelab
PhaseA_pos = Region[{ Stator_Ind_Am }];
PhaseB_pos = Region[{ Stator_Ind_Bm }];
PhaseC_pos = Region[{ Stator_Ind_Cp }];
Stator_IndsP = Region[{ Stator_Ind_Ap, Stator_Ind_Bp, Stator_Ind_Cp }];
Stator_IndsN = Region[{ Stator_Ind_Am, Stator_Ind_Bm, Stator_Ind_Cm }];
Stator_Inds = Region[ {PhaseA, PhaseB, PhaseC} ] ;
Rotor_Inds = Region[ {} ] ;
StatorC = Region[{ }] ;
StatorCC = Region[{ Stator_Fe }] ;
RotorC = Region[{ }] ;
RotorCC = Region[{ Rotor_Fe, Rotor_Magnets }] ;
// Moving band: with or without symmetry, these BND lines must be complete
Stator_Bnd_MB = #STATOR_BND_MOVING_BAND;
For k In {1:SymmetryFactor}
Rotor_Bnd_MB~{k} = Region[ (ROTOR_BND_MOVING_BAND+k-1) ];
Rotor_Bnd_MB += Region[ Rotor_Bnd_MB~{k} ];
EndFor
Rotor_Bnd_MBaux = Region[ {Rotor_Bnd_MB, -Rotor_Bnd_MB~{1}}];
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
Function {
mur_fe = 1000 ;
sigma_fe = 0 ;
NbrPhases = 3 ;
NbrPolePairs = NbrPolesTot/2 ;
DefineConstant[ b_remanent = { 1.2, Label "Remanent induction", Path "Input/3", Highlight "AliceBlue"} ] ;
// For a radial remanent b
For k In {1:nbMagnets}
br[ Rotor_Magnet~{k} ] = (-1)^(k-1) * b_remanent * Vector[ Cos[Atan2[Y[],X[]]], Sin[Atan2[Y[],X[]]], 0 ];
EndFor
Inominal = 3.9 ; // Nominal current
Tnominal = 2.5 ; // Nominal torque
//Data for modeling a stranded inductor
NbWires[] = 104 ; // Number of wires per slot
// STATOR_IND_AM comprises all the slots in that phase, we need thus to divide by the number of slots
nbSlots[] = Ceil[nbInds/NbrPhases/2] ;
SurfCoil[] = SurfaceArea[]{STATOR_IND_AM}/nbSlots[] ;//All inductors have the same surface
FillFactor_Winding = 0.5 ; // percentage of Cu in the surface coil side, smaller than 1
Factor_R_3DEffects = 1.5 ; // bigger than Adding 50% of resistance
DefineConstant[ rpm = { 500,
Label "speed in rpm",
Path "Input/7", Highlight "AliceBlue"} ]; // speed in rpm
wr = rpm/60*2*Pi ; // speed in rad_mec/s
// supply at fixed position
DefineConstant[ Freq = {wr*NbrPolePairs/(2*Pi), ReadOnly 1,
Path "Output/1", Highlight "LightYellow" } ];
Omega = 2*Pi*Freq ;
T = 1/Freq ;
DefineConstant[ thetaMax_deg = { 180, Label "End rotor angle (loop)",
Path "Input/21", Highlight "AliceBlue" } ];
theta0 = InitialRotorAngle + 0. ;
thetaMax = thetaMax_deg * deg2rad ; // end rotor angle (used in doing a loop)
DefineConstant[ NbTurns = { (thetaMax-theta0)/(2*Pi), Label "Number of revolutions",
Path "Input/24", Highlight "LightGrey", ReadOnly 1} ];
DefineConstant[ delta_theta_deg = { 1., Label "step in degrees",
Path "Input/22", Highlight "AliceBlue"} ];
delta_theta = delta_theta_deg * deg2rad ;
time0 = 0 ; // at initial rotor position
delta_time = delta_theta/wr;
timemax = thetaMax/wr;
DefineConstant[ NbSteps = { Ceil[(timemax-time0)/delta_time], Label "Number of steps",
Path "Input/23", Highlight "LightGrey", ReadOnly 1} ];
RotorPosition[] = InitialRotorAngle + $Time * wr ;
RotorPosition_deg[] = RotorPosition[]*180/Pi;
Flag_ParkTransformation = 1 ;
Theta_Park[] = ((RotorPosition[] + Pi/8) - Pi/6) * NbrPolePairs; // electrical degrees
Theta_Park_deg[] = Theta_Park[]*180/Pi;
DefineConstant[ ID = { 0, Path "Input/60", Label "Id stator current", Highlight "AliceBlue"},
IQ = { Inominal, Path "Input/61", Label "Iq stator current", Highlight "AliceBlue"},
I0 = { 0, Visible 0} ] ;
If(Flag_SrcType_Stator==0)
UndefineConstant["Input/60ID"];
UndefineConstant["Input/61IQ"];
EndIf
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
Dir="res/";
ExtGmsh = ".pos";
ExtGnuplot = ".dat";
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
If(Flag_SrcType_Stator==1)
UndefineConstant["Input/ZR"];
EndIf
If(Flag_Cir)
Include "pmsm_8p_circuit.pro" ;
EndIf
Include "machine_magstadyn_a.pro" ;
DefineConstant[ ResolutionChoices = {"TimeDomain_Loop", Path "GetDP/1"} ];
DefineConstant[ PostOperationChoices = {"Map_LocalFields", Path "GetDP/2"} ];
DefineConstant[ ComputeCommand = {"-solve -v 1 -v2", Path "GetDP/9"} ];
//
// Permanent Magnet Synchronous Generator
//
Include "pmsm_data.geo";
DefineConstant[ Flag_NL = {0,
Choices{ 0="Linear",
1="Nonlinear BH curve"},
Label "Fe magnetic law",
Path "Input/3", Highlight "Blue"} ] ;
DefineConstant[ Flag_SrcType_Stator = {1,
Choices{ 0="None",
1="Current" },
Label "Source Type in Stator",
Path "Input/4", Highlight "Blue", Visible 1} ] ;
DefineConstant[ Flag_SrcType_Rotor = {0,
Choices{ 0="None",
1="Current" },
Label "Source Type in Rotor",
Path "Input/5", Highlight "Blue", Visible 0} ] ;
DefineConstant[ Flag_Cir = {!Flag_SrcType_Stator, Choices{0,1},
Label "Use circuit in Stator", ReadOnly 1, Visible 0} ] ;
Group {
Stator_Fe = #STATOR_FE ;
Stator_Al = #{};
Stator_Cu = #{};
Stator_Air = #STATOR_AIR ;
Stator_Airgap = #STATOR_AIRGAP ;
Stator_Bnd_A0 = #STATOR_BND_A0 ;
Stator_Bnd_A1 = #STATOR_BND_A1 ;
Rotor_Fe = #ROTOR_FE ;
Rotor_Al = #{};
Rotor_Cu = #{};
Stator_Air = #STATOR_AIR ;
Stator_Airgap = #STATOR_AIRGAP ;
Stator_Bnd_MB = #STATOR_BND_MOVING_BAND ;
Stator_Bnd_A0 = #STATOR_BND_A0 ;
Stator_Bnd_A1 = #STATOR_BND_A1 ;
Rotor_Fe = #ROTOR_FE ;
Rotor_Air = #ROTOR_AIR ;
Rotor_Airgap = #ROTOR_AIRGAP ;
Rotor_Bnd_A0 = #ROTOR_BND_A0 ;
Rotor_Bnd_A1 = #ROTOR_BND_A1 ;
MovingBand_PhysicalNb = #MOVING_BAND ; // Fictitious number for moving band, not in the geo file
Surf_Inf = #SURF_EXT ;
Surf_bn0 = #SURF_INT ;
Surf_cutA0 = #{STATOR_BND_A0, ROTOR_BND_A0};
Surf_cutA1 = #{STATOR_BND_A1, ROTOR_BND_A1};
Dummy = #NICEPOS;
nbMagnets = NbrPolesTot/SymmetryFactor ;
For k In {1:nbMagnets}
Rotor_Magnet~{k} = Region[ (ROTOR_MAGNET+k-1) ];
Rotor_Magnets += Region[ Rotor_Magnet~{k} ];
EndFor
nbInds = (Flag_Symmetry) ? NbrPoles*NbrSectTotStator/NbrPolesTot : NbrSectTotStator ;
Printf("NbrPoles=%g, nbInds=%g SymmetryFactor=%g", NbrPoles, nbInds, SymmetryFactor);
Stator_Ind_Ap = #{}; Stator_Ind_Am = #{STATOR_IND_AM};
Stator_Ind_Bp = #{}; Stator_Ind_Bm = #{STATOR_IND_BM};
Stator_Ind_Cp = #{STATOR_IND_CP}; Stator_Ind_Cm = #{};
If(NbrPoles > 1)
Stator_Ind_Ap += #STATOR_IND_AP;
Stator_Ind_Bp += #STATOR_IND_BP;
Stator_Ind_Cm += #STATOR_IND_CM;
EndIf
PhaseA = Region[{ Stator_Ind_Ap, Stator_Ind_Am }];
PhaseB = Region[{ Stator_Ind_Bp, Stator_Ind_Bm }];
PhaseC = Region[{ Stator_Ind_Cp, Stator_Ind_Cm }];
// Provisional: Just one physical region for nice graph in Onelab
PhaseA_pos = Region[{ Stator_Ind_Am }];
PhaseB_pos = Region[{ Stator_Ind_Bm }];
PhaseC_pos = Region[{ Stator_Ind_Cp }];
Stator_IndsP = Region[{ Stator_Ind_Ap, Stator_Ind_Bp, Stator_Ind_Cp }];
Stator_IndsN = Region[{ Stator_Ind_Am, Stator_Ind_Bm, Stator_Ind_Cm }];
Stator_Inds = Region[ {PhaseA, PhaseB, PhaseC} ] ;
Rotor_Inds = Region[ {} ] ;
StatorC = Region[{ }] ;
StatorCC = Region[{ Stator_Fe }] ;
RotorC = Region[{ }] ;
RotorCC = Region[{ Rotor_Fe, Rotor_Magnets }] ;
// Moving band: with or without symmetry, these BND lines must be complete
Stator_Bnd_MB = #STATOR_BND_MOVING_BAND;
For k In {1:SymmetryFactor}
Rotor_Bnd_MB~{k} = Region[ (ROTOR_BND_MOVING_BAND+k-1) ];
Rotor_Bnd_MB += Region[ Rotor_Bnd_MB~{k} ];
EndFor
Rotor_Bnd_MBaux = Region[ {Rotor_Bnd_MB, -Rotor_Bnd_MB~{1}}];
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
Function {
mur_fe = 1000 ;
sigma_fe = 0 ;
NbrPhases = 3 ;
NbrPolePairs = NbrPolesTot/2 ;
DefineConstant[ b_remanent = { 1.2, Label "Remanent induction", Path "Input/3", Highlight "AliceBlue"} ] ;
// For a radial remanent b
For k In {1:nbMagnets}
br[ Rotor_Magnet~{k} ] = (-1)^(k-1) * b_remanent * Vector[ Cos[Atan2[Y[],X[]]], Sin[Atan2[Y[],X[]]], 0 ];
EndFor
Inominal = 3.9 ; // Nominal current
Tnominal = 2.5 ; // Nominal torque
//Data for modeling a stranded inductor
NbWires[] = 104 ; // Number of wires per slot
// STATOR_IND_AM comprises all the slots in that phase, we need thus to divide by the number of slots
nbSlots[] = Ceil[nbInds/NbrPhases/2] ;
SurfCoil[] = SurfaceArea[]{STATOR_IND_AM}/nbSlots[] ;//All inductors have the same surface
FillFactor_Winding = 0.5 ; // percentage of Cu in the surface coil side, smaller than 1
Factor_R_3DEffects = 1.5 ; // bigger than Adding 50% of resistance
DefineConstant[ rpm = { 500,
Label "speed in rpm",
Path "Input/7", Highlight "AliceBlue"} ]; // speed in rpm
wr = rpm/60*2*Pi ; // speed in rad_mec/s
// supply at fixed position
DefineConstant[ Freq = {wr*NbrPolePairs/(2*Pi), ReadOnly 1,
Path "Output/1", Highlight "LightYellow" } ];
Omega = 2*Pi*Freq ;
T = 1/Freq ;
DefineConstant[ thetaMax_deg = { 180, Label "End rotor angle (loop)",
Path "Input/21", Highlight "AliceBlue" } ];
theta0 = InitialRotorAngle + 0. ;
thetaMax = thetaMax_deg * deg2rad ; // end rotor angle (used in doing a loop)
DefineConstant[ NbTurns = { (thetaMax-theta0)/(2*Pi), Label "Number of revolutions",
Path "Input/24", Highlight "LightGrey", ReadOnly 1} ];
DefineConstant[ delta_theta_deg = { 1., Label "step in degrees",
Path "Input/22", Highlight "AliceBlue"} ];
delta_theta = delta_theta_deg * deg2rad ;
time0 = 0 ; // at initial rotor position
delta_time = delta_theta/wr;
timemax = thetaMax/wr;
DefineConstant[ NbSteps = { Ceil[(timemax-time0)/delta_time], Label "Number of steps",
Path "Input/23", Highlight "LightGrey", ReadOnly 1} ];
RotorPosition[] = InitialRotorAngle + $Time * wr ;
RotorPosition_deg[] = RotorPosition[]*180/Pi;
Flag_ParkTransformation = 1 ;
Theta_Park[] = ((RotorPosition[] + Pi/8) - Pi/6) * NbrPolePairs; // electrical degrees
Theta_Park_deg[] = Theta_Park[]*180/Pi;
DefineConstant[ ID = { 0, Path "Input/60", Label "Id stator current", Highlight "AliceBlue"},
IQ = { Inominal, Path "Input/61", Label "Iq stator current", Highlight "AliceBlue"},
I0 = { 0, Visible 0} ] ;
If(Flag_SrcType_Stator==0)
UndefineConstant["Input/60ID"];
UndefineConstant["Input/61IQ"];
EndIf
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
Dir="res/";
ExtGmsh = ".pos";
ExtGnuplot = ".dat";
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
If(Flag_SrcType_Stator==1)
UndefineConstant["Input/ZR"];
EndIf
If(Flag_Cir)
Include "pmsm_8p_circuit.pro" ;
EndIf
Include "machine_magstadyn_a.pro" ;
DefineConstant[ ResolutionChoices = {"TimeDomain_Loop", Path "GetDP/1"} ];
DefineConstant[ PostOperationChoices = {"Map_LocalFields", Path "GetDP/2"} ];
DefineConstant[ ComputeCommand = {"-solve -v 1 -v2", Path "GetDP/9"} ];
//--------------------------------------------------------------------------------
// Rotor PMSM
//--------------------------------------------------------------------------------
A = InitialRotorAngle-45/2*deg2rad + A1; // with Theta_Park
sinA = Sin(A); cosA = Cos(A);
pntR[]+=newp; Point(newp)={rR1*cosA, rR1*sinA, 0, pR1};
pntR[]+=newp; Point(newp)={rR2*cosA, rR2*sinA, 0, pR1};
pntR[]+=newp; Point(newp)={rR4*cosA, rR4*sinA, 0, pR1};
pntR[]+=newp; Point(newp)={rR5*cosA, rR5*sinA, 0, pR1};
pntR[]+=newp; Point(newp)={rB1*cosA, rB1*sinA, 0, pR2};
For k In {0:#pntR[]-2}
linR0[]+=newl; Line(newl) = {pntR[k], pntR[k+1]};
EndFor
Transfinite Line{linR0[0]} = Ceil[(rR2-rR1)/pR1] ;
Transfinite Line{linR0[1]} = Ceil[(rR4-rR2)/pR1] ;
Transfinite Line{linR0[2]} = Ceil[(rR5-rR4)/pR1] ;
Transfinite Line{linR0[3]} = Ceil[(rB1-rR5)/pR1] ;
For k In {0:#linR0[]-1}
linR1[] += Rotate {{0, 0, 1}, {0, 0, 0}, A0+A1} { Duplicata{Line{linR0[k]};} };
EndFor
AA[] = {(A0-Th_magnet)/2+A1, Th_magnet, (A0-Th_magnet)/2+A1} ;
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, AA[0]} { Point{pntR[0]}; };
cirR[]+=lin[1];
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, AA[1]} { Point{lin[0]}; };
cirR[]+=lin[1];
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, AA[2]} { Point{lin[0]}; };
cirR[]+=lin[1];
surfint[]=cirR[{0,1,2}] ; // boundary conditions
pMagnet[] = Rotate {{0, 0, 1}, {0, 0, 0}, AA[0]} { Duplicata{Point{pntR[1]};} };
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, AA[1]} { Point{pMagnet[0]}; };
pMagnet[] += lin[0];
cirR[] += lin[1] ;
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, AA[0]} { Point{pntR[2]}; };
cirR[]+=lin[1]; pMagnet[] += lin[0];
pMagnet[] += Rotate {{0, 0, 1}, {0, 0, 0}, AA[1]} { Duplicata{Point{lin[0]};} };
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, AA[2]} { Point{pMagnet[3]}; };
cirR[]+=lin[1];
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, AA[0]} { Point{pntR[3]}; };
cirR[]+=lin[1];
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, AA[1]} { Point{lin[0]}; };
cirR[]+=lin[1];
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, AA[2]} { Point{lin[0]}; };
cirR[]+=lin[1];
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, A0+A1} { Point{pntR[4]}; };
cirR[]+=lin[1];
linR2[] = Rotate {{0, 0, 1}, {0, 0, 0}, (A0-Th_magnet)/2+A1} { Duplicata{Line{linR0[{1,2}]};} };
linR3[] = Rotate {{0, 0, 1}, {0, 0, 0},-(A0-Th_magnet)/2+A1} { Duplicata{Line{linR1[{1,2}]};} };
// surfaces rotor
Line Loop(newll) = {linR0[{0,1}], cirR[4], -linR2[0], cirR[3], linR3[0], cirR[5], -linR1[{1,0}], -cirR[{2,1,0}]};
srotor[0]=news; Plane Surface(srotor[0]) = {newll-1};
Line Loop(newl) = {linR2[1], cirR[7], -linR3[{1,0}], -cirR[3], linR2[0]};
smagnet[0]=news; Plane Surface(smagnet[0]) = {newll-1};
nn = #cirR[]-1 ;
Line Loop(newll) = {cirR[{nn-5}], linR2[1], -cirR[{nn-3}], -linR0[2]};
sairrotor[]+=news; Plane Surface(news) = {newll-1};
Line Loop(newll) = {cirR[{nn-4}], linR1[2], -cirR[{nn-1}], -linR3[1]};
sairrotor[]+=news; Plane Surface(news) = {newll-1};
Line Loop(newll) = {linR0[3], cirR[nn], -linR1[3], -cirR[{nn-1:nn-3:-1}]};
sairrotormb[]+=news; Plane Surface(news) = {newll-1};
// -------------------------------------------------------------------------------
// Moving band == AirGap rotor side
// -------------------------------------------------------------------------------
Transfinite Line{cirR[nn]} = NbrDivMB+1 ;
//Filling the gap for the whole 2*Pi
lineMBrotor[]=cirR[{nn}];
For k In {1:NbrPolesTot-1}
lineMBrotoraux[]+=Rotate {{0, 0, 1}, {0, 0, 0}, k*A0} { Duplicata{Line{lineMBrotor[]};} };
EndFor
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
If(SymmetryFactor<8)
// FULL MODEL ==> Rotation of NbrPolesTot*Pi/4
// For simplicity: rotating first the interior and exterior boundaries
If (SymmetryFactor>1)
For k In {0:#linR1[]-1}
linR1_[] += Rotate {{0, 0, 1}, {0, 0, 0}, 2*Pi/SymmetryFactor-Pi/4} { Duplicata{Line{linR1[k]};} };
EndFor
linR1[] = linR1_[];
EndIf
For k In {1:NbrPoles-1}
surfint[] += Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Line{surfint[{0:2}]};} };
EndFor
For k In {1:NbrPoles-1}
srotor[] += Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Surface{srotor[0]};} };
smagnet[]+= Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Surface{smagnet[0]};} };
sairrotor[] += Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Surface{sairrotor[{0,1}]};} };
sairrotormb[]+= Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Surface{sairrotormb[0]};} };
EndFor
EndIf
// -------------------------------------------------------------------------------
// Physical regions
// -------------------------------------------------------------------------------
Physical Surface(ROTOR_FE) = {srotor[]}; // Rotor
Physical Surface(ROTOR_AIR) = {sairrotor[]}; // AirRotor
Physical Surface(ROTOR_AIRGAP) = {sairrotormb[]};// AirRotor for possible torque computation with Maxwell stress tensor
NN = (Flag_Symmetry)?NbrPoles:NbrPolesTot;
For k In {0:NN-1}
Physical Surface(ROTOR_MAGNET+k) = {smagnet[k]}; // Magnets
EndFor
Physical Line(SURF_INT) = {surfint[]}; // SurfInt
If(Flag_Symmetry) //Lines for symmetry link
Physical Line(ROTOR_BND_A0) = linR0[];
Physical Line(ROTOR_BND_A1) = linR1[];
EndIf
lineMBrotor[] += lineMBrotoraux[] ;
If(!Flag_Symmetry)
Physical Line(ROTOR_BND_MOVING_BAND) = {lineMBrotor[]};
EndIf
If(Flag_Symmetry)
nr = #lineMBrotor[];
nnp = nr/(NbrPolesTot/NbrSect) ;
For k In {1:Floor[NbrPolesTot/NbrSect]}
kk= ((k*nnp-1) > nr) ? nr-1 : k*nnp-1 ;
Physical Line(ROTOR_BND_MOVING_BAND+k-1) = lineMBrotor[{(k-1)*nnp:kk}] ;
EndFor
k1 = Floor[NbrPolesTot/NbrSect];
k2 = Ceil[NbrPolesTot/NbrSect];
If (k2 > k1)
Physical Line(ROTOR_BND_MOVING_BAND+k2-1) = lineMBrotor[{(k2-1)*nnp:#lineMBrotor[]-1}] ;
EndIf
EndIf
// For nice visualisation...
linRotor[] = CombinedBoundary{Surface{srotor[]};};
linMagnet[] = Boundary{Surface{smagnet[]};};
nicepos_rotor[] += { linRotor[], linMagnet[] };
Color SteelBlue {Surface{srotor[]};}
Color SkyBlue {Surface{sairrotor[], sairrotormb[]};}
Color Orchid {Surface{smagnet[{0:#smagnet[]-1:2}]};}
If(#smagnet[]>1)
Color Purple {Surface{smagnet[{1:#smagnet[]-1:2}]};}
EndIf
// -------------------------------------------------------------------------------
// Moving band == AirGap stator side
// -------------------------------------------------------------------------------
pntG[]+=newp; Point(newp) = {rB2, 0., 0., pS1}; // aligned with the stator
circ[] = Extrude {{0, 0, 1}, {0, 0, 0}, A0} { Point{pntG[0]}; };
pntG[]+=circ[0];
lineMBstator[]=circ[1];
Transfinite Line{lineMBstator[0]} = NbrDivMB+1 ;
//Filling the gap for the whole 2*Pi
For k In {1:NbrPolesTot-1}
lineMBstatoraux[]+= Rotate {{0, 0, 1}, {0, 0, 0}, k*A0} { Duplicata{Line{lineMBstator[0]};} };
EndFor
// -------------------------------------------------------------------------------
// Stator
// -------------------------------------------------------------------------------
pntS[] = newp; Point(newp)={rS1, 0, 0, pS1};
linS[] = newl; Line(newl) = {pntG[0], pntS[0]};
linS[]+= Rotate {{0, 0, 1}, {0, 0, 0}, A0} { Duplicata{Line{linS[0]};} };
pntS[]+=newp; Point(newp)={rS7,0,0,pS2};
points[]=Boundary{Line{linS[1]};};
pntS[]+=points[1];
lin[] = Extrude {{0, 0, 1}, {0, 0, 0}, A0} { Point{pntS[1]}; };
cirS[]= lin[1]; pntS[]+=lin[0];
linS[]+=newl; Line(newl) = {pntS[0], pntS[1]};
linS[]+=newl; Line(newl) = {pntS[2], pntS[3]};
// -------------------------------------------------------------------------------
// Slots
// -------------------------------------------------------------------------------
A2 = 0.0;
AA[]=deg2rad*{2.77+A2, 4.0+A2, 5.52+A2, 5.56+A2, 5.65+A2, 9.35+A2, 9.44+A2, 9.48+A2, 11+A2, 12.23+A2} ;
For k In {0:#AA[]-1}
cosAA[]+=Cos(AA[k]); sinAA[]+=Sin(AA[k]);
EndFor
pntSlot[]+=newp; Point(newp)={rS5*cosAA[0], rS5*sinAA[0], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS3*cosAA[1], rS3*sinAA[1], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS1*cosAA[2], rS1*sinAA[2], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS2*cosAA[3], rS2*sinAA[3], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS4*cosAA[3], rS4*sinAA[3], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS6*cosAA[4], rS6*sinAA[4], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS6*cosAA[5], rS6*sinAA[5], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS4*cosAA[6], rS4*sinAA[6], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS2*cosAA[6], rS2*sinAA[6], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS1*cosAA[7], rS1*sinAA[7], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS3*cosAA[8], rS3*sinAA[8], 0., pS3};
pntSlot[]+=newp; Point(newp)={rS5*cosAA[9], rS5*sinAA[9], 0., pS3};
// air slot 1
linASlot[]+=newl ; Line(newl)={pntSlot[2], pntSlot[3]};
linASlot[]+=newl ; Line(newl)={pntSlot[3], pntSlot[1]};
linASlot[]+=newl ; Circle(newl)={pntSlot[1], cen, pntSlot[10]};
linASlot[]+=newl ; Line(newl)={pntSlot[10], pntSlot[8]};
linASlot[]+=newl ; Line(newl)={pntSlot[8], pntSlot[9]};
linASlot[]+=newl ; Circle(newl)={pntSlot[9], cen, pntSlot[2]};
Line Loop(newll) = {linASlot[]};
sairslot[] += news ; Plane Surface(sairslot[0]) = {newll-1};
// coil slot 1
linSlot[]+=newl ; Line(newl)={pntSlot[1], pntSlot[0]};
linSlot[]+=newl ; Circle(newl)= {pntSlot[0], pntSlot[4], pntSlot[5]};
linSlot[]+=newl ; Line(newl)={pntSlot[5], pntSlot[6]};
linSlot[]+=newl ; Circle(newl)={pntSlot[6], pntSlot[7],pntSlot[11]};
linSlot[]+=newl ; Line(newl)={pntSlot[11], pntSlot[10]};
Line Loop(newll) = {-linASlot[2],linSlot[]};
sslot[] += news ; Plane Surface(sslot[0]) = {newll-1};
// slots 2 and 3
A2 = 15*deg2rad;
pntSlot0[0] = pntSlot[2];
pntSlot1[0] = pntSlot[9];
For k In{1:2}
pntSlot0[] += Rotate {{0, 0, 1}, {0, 0, 0}, A2} { Duplicata{Point{pntSlot0[k-1]};} };
pntSlot1[] += Rotate {{0, 0, 1}, {0, 0, 0}, A2} { Duplicata{Point{pntSlot1[k-1]};} };
EndFor
For k In{1:2}
sslot[] += Rotate {{0, 0, 1}, {0, 0, 0}, A2} { Duplicata{Surface{sslot[k-1]};} };
sairslot[] += Rotate {{0, 0, 1}, {0, 0, 0}, A2} { Duplicata{Surface{sairslot[k-1]};} };
EndFor
cSlot[]+=newl; Circle(newl) = {pntS[0], cen, pntSlot[2]};
cSlot[]+=newl; Circle(newl) = {pntSlot1[0], cen, pntSlot0[1]};
cSlot[]+=newl; Circle(newl) = {pntSlot1[1], cen, pntSlot0[2]};
cSlot[]+=newl; Circle(newl) = {pntSlot1[2], cen, pntS[2]};
linesslot0[] = CombinedBoundary{ Surface{ sslot[0], sairslot[0] } ;};
linesslot1[] = CombinedBoundary{ Surface{ sslot[1], sairslot[1] } ;};
linesslot2[] = CombinedBoundary{ Surface{ sslot[2], sairslot[2] } ;};
Line Loop(newll) = {-lineMBstator[0],linS[0], cSlot[0],-linesslot0[{4}],
cSlot[1],-linesslot1[{9}],
cSlot[2],-linesslot2[{9}], cSlot[3], -linS[1]};
sairgapS[0]=news; Plane Surface(sairgapS[0]) = {newll-1};
linesslot0[] -= linesslot0[{4}];
linesslot1[] -= linesslot1[{9}];
linesslot2[] -= linesslot2[{9}];
Line Loop(newll) = { cSlot[0], linesslot0[],
cSlot[1], linesslot1[],
cSlot[2], linesslot2[],
cSlot[3], linS[3], -cirS[0], -linS[2]};
sstator[0]=news; Plane Surface(sstator[0]) = {newll-1};
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
auxlink[]=linS[{1,3}]; // A1
If(SymmetryFactor<8)
// FULL MODEL ==> Rotation of NbrPolesTot*Pi/4
// For simplicity: rotating the interior and exterior boundaries
If (SymmetryFactor>1)
For k In {0:#auxlink[]-1}
auxlink_[] += Rotate {{0, 0, 1}, {0, 0, 0}, 2*Pi/SymmetryFactor-Pi/4} { Duplicata{Line{auxlink[k]};} };
EndFor
auxlink[] = auxlink_[];
EndIf
For k In {1:NbrPoles-1}
cirS[] += Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Line{cirS[{0}]};} };
EndFor
For k In {1:NbrPoles-1}
sstator[]+= Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Surface{sstator[0]};} };
sairgapS[]+= Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Surface{sairgapS[0]};} };
sairslot[]+= Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Surface{sairslot[{0:2}]};} };
sslot[]+= Rotate {{0, 0, 1}, {0, 0, 0}, k*Pi/4} { Duplicata{ Surface{sslot[{0:2}]};} };
EndFor
EndIf
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
// Physical regions
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
Physical Surface(STATOR_FE) = {sstator[]}; // Stator
Physical Surface(STATOR_AIR) = {sairslot[]}; // AirStator
Physical Surface(STATOR_AIRGAP) = {sairgapS[]}; // AirStator for possible torque computation with Maxwell stress tensor
NN = (Flag_Symmetry)?NbrSectStator:NbrSectTotStator;
//For k In {0:NN-1}
// Physical Surface(STATOR_IND+k) = {sslot[k]}; //Inds
//EndFor
Physical Surface(STATOR_IND_AM) = {sslot[{0:NN-1:6}]};
Physical Surface(STATOR_IND_CP) = {sslot[{1:NN-1:6}]};
Physical Surface(STATOR_IND_BM) = {sslot[{2:NN-1:6}]};
If(NbrSectStator>2)
Physical Surface(STATOR_IND_AP) = {sslot[{3:NN-1:6}]};
Physical Surface(STATOR_IND_CM) = {sslot[{4:NN-1:6}]};
Physical Surface(STATOR_IND_BP) = {sslot[{5:NN-1:6}]};
EndIf
Color Pink {Surface{ sslot[{0:NN-1:6}] };} // A-
Color ForestGreen {Surface{ sslot[{1:NN-1:6}] };} // C+
Color PaleGoldenrod{Surface{ sslot[{2:NN-1:6}] };} // B-
If (#sslot[]>=6)
Color Red {Surface{ sslot[{3:NN-1:6}] };} // A+
Color SpringGreen{Surface{ sslot[{4:NN-1:6}] };} // C-
Color Gold {Surface{ sslot[{5:NN-1:6}] };} // B+
EndIf
Physical Line(SURF_EXT) = {cirS[]}; // SurfExt
If(Flag_Symmetry) //Lines for symmetry link
Physical Line(STATOR_BND_A0) = linS[{0,2}];
Physical Line(STATOR_BND_A1) = auxlink[] ;
EndIf
lineMBstator[] += lineMBstatoraux[] ;
If(!Flag_Symmetry)
Physical Line(STATOR_BND_MOVING_BAND) = {lineMBstator[]};
EndIf
If(Flag_Symmetry)
ns = #lineMBstator[];
nns = ns/SymmetryFactor ;
For k In {1:SymmetryFactor}
kk= ((k*nns-1) > ns) ? ns-1 : k*nns-1 ;
Physical Line(STATOR_BND_MOVING_BAND+k-1) = {lineMBstator[{(k-1)*nns:kk}]};
EndFor
k1 = Floor[NbrPolesTot/NbrSect];
k2 = Ceil[NbrPolesTot/NbrSect];
If (k2 > k1)
Physical Line(STATOR_BND_MOVING_BAND+k2-1) = lineMBstator[{(k2-1)*nns:#lineMBstator[]-1}] ;
EndIf
EndIf
// For nice visualisation...
linStator[] = CombinedBoundary{Surface{sstator[]};};
linSlot[] = CombinedBoundary{Surface{sslot[]};};
nicepos_stator[] += {linStator[],linSlot[] };
Color SteelBlue {Surface{sstator[]};}
Color SkyBlue {Surface{sairslot[],sairgapS[]};}
Point(1) = {0,0,0};
DefineConstant
[
n0 = {3,
Label "Number",
Path "Test widgets/Numbers"}
n01 = {3, Min 1, Max 4, Step 1,
Label "Number (with range)",
Path "Test widgets/Numbers"}
n1 = {3, ReadOnly 1,
Label "Number (read only)",
Path "Test widgets/Numbers"}
n12 = {3, ReadOnlyRange 1, Min 1, Max 4, Step 1,
Label "Number (read only range)",
Path "Test widgets/Numbers"}
n2 = {3, Choices{1, 2, 3, 4},
Label "Number (with choices)",
Path "Test widgets/Numbers"}
n3 = {1, Choices{0,1},
Label "Number (with binary choice) - Show String Options?",
Path "Test widgets/Numbers"},
n4 = {3, Choices{1 = "One",
2 = "Two",
3 = "Three",
4 = "Go!!"},
Label "Number (with enumeration)",
Path "Test widgets/Numbers"}
];
Printf("N3 = %g", n3);
If(n3) // test define/undefine
Printf("N3 IS SET");
DefineConstant[
s0 = {"a",
Label "String",
Path "Test widgets/Strings"}
s1 = {"a", ReadOnly 1,
Label "String (read only)",
Path "Test widgets/Strings"}
s2 = {"a", Choices {"a","b","c"},
Label "String with choices",
Path "Test widgets/Strings"}
s20 = {"a, c", Choices {"a","b","c"}, MultipleSelection "101",
Label "String with multiple selection",
Path "Test widgets/Strings"}
s3 = {"a.txt", Choices{"a.txt","b.txt","c.txt"}, Kind "file",
Label "File",
Path "Test widgets/Strings"}
s4 = {"a.txt", Kind "file", Macro "Gmsh",
Label "Macro",
Path "Test widgets/Strings"}
];
EndIf
If(!n3)
Printf("N3 IS **NOT** SET");
UndefineConstant[ "Test widgets/Strings/s0",
"Test widgets/Strings/s1",
"Test widgets/Strings/s2",
"Test widgets/Strings/s20",
"Test widgets/Strings/s3",
"Test widgets/Strings/s4"];
EndIf
<resources>
<style name="AppTheme" parent="android:Theme.Holo.Light" />
</resources>
\ No newline at end of file
<resources>
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar" />
</resources>
\ No newline at end of file
<resources>
<string name="app_name">Onelab</string>
<string name="button_open_external_file">Open MSH file on external storage</string>
<string name="button_ok">OK</string>
<string name="button_recalculate_param">Recalulate with new parameters</string>
<string name="dialog_title_choosefile">Choose a MSH file</string>
<string name="error_nosdcard">No SDcard found on your device</string>
<string name="title_parameter">Settings</string>
<string name="title_activity_main">Onelab</string>
<string name="error_nomshfile">No compatible files or directories in this folder</string>
<string name="menu_list">Load a model</string>
<string name="menu_settings">Settings</string>
<string name="menu_model">Show the model</string>
<string name="menu_postpro">Post-processing</string>
<string name="menu_hidebar">Hide the top bar</string>
<string name="menu_showbar">Show the top bar</string>
<string name="menu_view">Change the view</string>
<string name="menu_view_x">Set X view</string>
<string name="menu_view_y">Set Y view</string>
<string name="menu_view_z">Set Z view</string>
<string name="menu_view_scale">Reset unit scale</string>
<string name="menu_view_translation">Center the model</string>
</resources>
\ No newline at end of file
<resources>
<style name="AppTheme" parent="android:Theme.Light" />
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<models>
<model>
<title>Magnet</title>
<summary>Simple magnet example</summary>
<file type="geo">magnet.geo</file>
</model>
<model>
<title>Eight-pole permanent magnet synchronous machine</title>
<summary></summary>
<file type="geo">pmsm.geo</file>
</model>
<model>
<title>Test</title>
<summary>test all parameters of onelab</summary>
<file type="geo">test.geo</file>
</model>
</models>
package org.geuz.onelab;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Handler;
public class GLESRender implements Renderer{
Gmsh mGModel;
Handler loadingHand;
public GLESRender(Gmsh model) {
this.mGModel = model;
}
public void load(String filename){
mGModel.load(filename);
}
public void translate(float tx, float ty, float tz){
mGModel.translation(tx, ty, tz);
}
public void scale(float sx, float sy, float sz){
mGModel.scale(sx, sy, sz);
}
public void rotate(float rx, float ry, float rz){
mGModel.rotate(rx, ry, rz);
}
// OpenGL ES methods
public void onDrawFrame(GL10 gl) {
mGModel.viewDraw();
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
mGModel.viewInit(width, height);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}
}
package org.geuz.onelab;
import android.os.Handler;
public class Gmsh {
/** From C / C++ code **/
static {
System.loadLibrary("f2cblas");
System.loadLibrary("f2clapack");
System.loadLibrary("petsc");
System.loadLibrary("Gmsh");
System.loadLibrary("GetDP");
System.loadLibrary("Onelab");
}
private native long init(String name); // Init Gmsh
private native void loadFile(long ptr, String name); // load a file(OpenProjet)
private native void initView(long ptr, int w, int h); // Called each time the GLView change
private native void drawView(long ptr); // Called each time the GLView request a render
private native void setTranslation(long ptr, float tx, float ty, float tz); // translate the current GModel
private native void setScale(long ptr, float sx, float sy, float sz); // scale the current GModel
private native void setRotate(long ptr, float rx, float ry, float rz); // rotate the current GModel
private native void setShow(long ptr, String what, boolean show); // select what to show / hide
private native long getOnelabInstance(); // return the singleton of the onelab server
public native String[] getParams(); // return the parameters for onelab
public native int setParam(String type, String name, String value); // change a parameters
public native String[] getPView(); // get a list of PViews
public native void setPView(int position, int intervalsType,int visible,int nbIso); // Change options for a PView
public native int onelabCB(String action); // Call onelab
/** Java CLASS **/
private long ptr;
private long onelab;
private Handler handler;
public Gmsh(String name, Handler handler) {
ptr = this.init(name);
onelab = this.getOnelabInstance();
this.handler = handler;
}
public void viewInit(int w, int h) {
this.initView(ptr, w, h);
}
public void viewDraw() {
this.drawView(ptr);
}
public void load(String filename){
this.loadFile(ptr, filename);
}
public void translation(float tx, float ty, float tz)
{
this.setTranslation(ptr, tx, ty, tz);
}
public void scale(float sx, float sy, float sz)
{
this.setScale(ptr, sx, sy, sz);
}
public void rotate(float rx, float ry, float rz) {
this.setRotate(ptr, rx, ry, rz);
}
public void showGeom(boolean show)
{
this.setShow(ptr, "geom", show);
}
public void showMesh(boolean show)
{
this.setShow(ptr, "mesh", show);
}
public long getOnelab() {
return this.onelab;
}
public void ShowPopup(String message) {
handler.obtainMessage(0, message).sendToTarget();
}
public void RequestRender() {
handler.obtainMessage(1).sendToTarget();
}
public void SetLoading(String message) {
handler.obtainMessage(2, message).sendToTarget();
}
public void SetLoading(int percent) {
handler.obtainMessage(3, percent, 100).sendToTarget();
}
}
This diff is collapsed.
package org.geuz.onelab;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class ModeleArrayAdapter extends ArrayAdapter<String> {
private Context context;
private String[] titres;
private String[] descriptions;
public ModeleArrayAdapter(Context context, String[] titres) {
super(context, R.layout.model, titres);
this.context = context;
this.titres = titres;
}
public ModeleArrayAdapter(Context context, String[] titres, String[] descriptions) {
this(context,titres);
this.descriptions = descriptions;
}
public ModeleArrayAdapter(Context context, Models models) {
super(context, R.layout.model, models.getNames());
this.context = context;
titres = new String[models.size()];
descriptions = new String[models.size()];
for(int i=0;i < models.size();i++) {
titres[i] = models.getName(i);
descriptions[i] = models.getSummary(i);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.model, parent, false);
TextView Titre = (TextView) rowView.findViewById(R.id.titre);
TextView Description = (TextView) rowView.findViewById(R.id.description);
ImageView Icone = (ImageView) rowView.findViewById(R.id.icone);
if(this.titres != null) Titre.setText(this.titres[position]);
if(this.descriptions != null) Description.setText(this.descriptions[position]);
if(Icone != null) Icone.setImageResource(R.drawable.ic_launcher);
Icone.setPadding(10, 10, 10, 10);
return rowView;
}
}
package org.geuz.onelab;
import java.util.ArrayList;
import java.util.List;
class Models {
private List<String> _name;
private List<String> _summary;
private List<String> _file;
public Models(){
_name = new ArrayList<String>();
_summary = new ArrayList<String>();
_file = new ArrayList<String>();
}
public int size() {
return _name.size();
}
public String getName(int pos) {
return _name.get(pos);
}
public String[] getNames() {
String[] ret = new String[_name.size()];
ret = _name.toArray(ret);
return ret;
}
public String getSummary(int pos) {
return _summary.get(pos);
}
public String getFile(int pos) {
return _file.get(pos);
}
public void addModel(String name, String summary, String file){
_name.add(name);
_file.add(file);
_summary.add(summary);
}
}
package org.geuz.onelab;
import android.content.Context;
import android.graphics.Color;
import android.widget.LinearLayout;
import android.widget.TextView;
public class Parameter {
protected Context _context;
protected Gmsh _gmsh;
protected SeparatedListView _listView;
protected mGLSurfaceView _glView;
protected String _name;
protected String _label;
protected boolean _readOnly;
protected boolean _changed;
protected TextView _title;
public Parameter(Context context, Gmsh gmsh, mGLSurfaceView glView, String name){
_context = context;
_gmsh = gmsh;
_glView = glView;
_readOnly = false;
_name = name;
_title = new TextView(context);
_title.setText(name);
_title.setTextAppearance(context, android.R.style.TextAppearance_DeviceDefault_Medium);
_title.setTextColor(Color.DKGRAY);
}
public Parameter(Context context, Gmsh gmsh, mGLSurfaceView glView, String name, boolean readOnly){
this(context, gmsh, glView, name);
_readOnly = readOnly;
_changed = false;
}
protected void update(){
if(_label != null && !_label.equals(""))
_title.setText(_label);
else {
String tmp[] = _name.split("/");
_title.setText(tmp[tmp.length-1]);
}
if(isReadOnly()) _title.setAlpha(0.423f);
}
public void setName(String name) {_name = name;this.update();}
public void setReadOnly(boolean readOnly) {_readOnly = readOnly;this.update();}
public void setLabel(String label) {_label = label;this.update();}
public String getName() {return _name;}
public boolean isReadOnly() {return _readOnly;}
public String getLabel() {return _label;}
public int fromString(String s){
String[] infos = s.split("\n");
int pos=0;
pos++;// version
pos++;// type
setName(infos[pos++]);// name
setLabel(infos[pos++]);// label
pos++;// help
pos++;// never change
pos++;// changed
if(Integer.parseInt(infos[pos++]) != 1)return -1;// visible
this.setReadOnly((infos[pos++].equals("1")));// read only
int nAttributes = Integer.parseInt(infos[pos++]);// number of attributes
pos+=(nAttributes*2);// key+value
int nClients = Integer.parseInt(infos[pos++]);// number of client
pos+=nClients;// clients
this.update();
return pos;
}
public boolean changed() { if(_changed){_changed=false; return true;}return _changed;}
public String getType(){return "Parameter";}
public void setList(SeparatedListView list){ _listView = list;}
public LinearLayout getView() {
LinearLayout paramLayout = new LinearLayout(_context);
paramLayout.setOrientation(LinearLayout.VERTICAL);
paramLayout.addView(_title);
return paramLayout;
}
}
package org.geuz.onelab;
import java.util.ArrayList;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.Spinner;
public class ParameterNumber extends Parameter{
private double _value, _min, _max, _step;
private SeekBar _bar;
private ArrayList<Double> _values;
private ArrayList<String> _choices;
private ArrayAdapter<String> _adapter;
private Spinner _spinner;
private CheckBox _checkbox;
private EditText _edittext;
public ParameterNumber(Context context, Gmsh gmsh, mGLSurfaceView glView, String name){
super(context, gmsh, glView, name);
}
public ParameterNumber(Context context, Gmsh gmsh, mGLSurfaceView glView, String name, double value, double min, double max, double step)
{
this(context, gmsh, glView, name);
_value = value;
_min = min;
_max = max;
_step = step;
}
public ParameterNumber(Context context, Gmsh gmsh, mGLSurfaceView glView, String name, boolean readOnly, double value, double min, double max, double step)
{
this(context, gmsh, glView, name, value, min, max, step);
_readOnly = readOnly;
}
protected void update(){
super.update();
int nDecimal = String.valueOf(this.getStep()).length() - String.valueOf(this.getStep()).lastIndexOf('.') - 1; // hack for double round
if(_bar != null){
if(_label != null && !_label.equals(""))
_title.setText(_label + " (" + Math.round(_value*Math.pow(10, nDecimal))/Math.pow(10, nDecimal) + ")");
else {
String tmp[] = _name.split("/");
_title.setText(tmp[tmp.length-1] + " (" + Math.round(_value*Math.pow(10, nDecimal))/Math.pow(10, nDecimal) + ")");
}
_bar.setProgress((int) ((_value-_min)/_step));
_bar.setMax((int) ((_max-_min)/_step));
_bar.setEnabled(!this.isReadOnly());
}
else if(_spinner != null)
{
for(int i=0;i<_choices.size();i++)
if(_values.get(i) == _value)
_spinner.setSelection(i, true);
}
else if(_checkbox != null)
{
if(_label != null) _checkbox.setText(_label);
else _checkbox.setText(_name);
_checkbox.setChecked((_value == 0)? false : true);
}
else if(_edittext != null)
{
_edittext.setText(""+Math.round(_value*Math.pow(10, nDecimal))/Math.pow(10, nDecimal));
}
}
public void setValue(double value) {
if(value < _min || value > _max) {
Log.w("ParameterNumber", "Incorect value "+value+" (max="+_max+" min="+_min+")");
return;
}
if(value != _value)
_changed = true;
_value = value;
this.update();
}
public void setMin(double min) {_min = min;this.update();}
public void setMax(double max) {_max = max;this.update();}
public void setStep(double step) {_step = step;this.update();}
public void addChoice(double choice, String value) {
if(_values == null) {
_values = new ArrayList<Double>();
_choices = new ArrayList<String>();
_values.add(choice);
_choices.add(value);
if(_spinner == null) {
_spinner = new Spinner(_context);
_adapter = new ArrayAdapter<String>(_context, android.R.layout.simple_spinner_dropdown_item, _choices);
_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
_spinner.setAdapter(_adapter);
}
}
else
{
for(int i=0;i<_values.size();i++) {
if(_values.get(i).equals(choice) && _choices.size() > i) {
_choices.set(i, value);
return;
}
else if(_values.get(i).equals(choice))
{
_choices.add(value);
return;
}
}
_values.add(choice);
_choices.add(value);
}
}
public double getValue() {return _value;}
public double getMax() {return _max;}
public double getMin() {return _min;}
public double getStep() {return _step;}
public int fromString(String s){
int pos = super.fromString(s);
if(pos <= 0) return -1; // error
String[] infos = s.split("\n");
String tmpVal = infos[pos++];
if(tmpVal.equals("Inf")) // TODO set value to max ???
_value = 1;
else
_value = Double.parseDouble(tmpVal);
this.setMin(Double.parseDouble(infos[pos++]));
this.setMax(Double.parseDouble(infos[pos++]));
this.setStep(Double.parseDouble(infos[pos++]));
pos++;// index
int nChoix = Integer.parseInt(infos[pos++]); // choices' size
double choices[] = new double[nChoix];
for(int i=0; i<nChoix; i++)
if(nChoix == 2)
choices[i] = Double.parseDouble(infos[pos++]); // choice
else pos++;
int nLabels = Integer.parseInt(infos[pos++]); // labels' size
if(nChoix == 2 && choices[0] == 0 && choices[1] == 1 && nLabels == 0) {
_checkbox = new CheckBox(_context);
this.update();
return pos;
}
for(int i=0; i<nLabels && nChoix == nLabels; i++)
{
double val = Double.parseDouble(infos[pos++]); // choice
this.addChoice(val, infos[pos++]); // label
}
// ...
if(nLabels < 1 && _step == 0)
_edittext = new EditText(_context);
else if(nLabels < 1)
_bar = new SeekBar(_context);
this.update();
return pos;
}
public String getType(){return "ParameterNumber";}
public LinearLayout getView(){
LinearLayout paramLayout = new LinearLayout(_context);
paramLayout.setOrientation(LinearLayout.VERTICAL);
paramLayout.addView(_title);
if(_spinner != null) {
paramLayout.addView(_spinner);
_spinner.setEnabled(!_readOnly);
_spinner.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if(_listView != null) _listView.refresh();
}
});
_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onNothingSelected(AdapterView<?> arg0) {}
public void onItemSelected(AdapterView<?> parent, View view,
int pos, long id) {
if(_listView != null) _listView.refresh();
setValue(_values.get(pos));
_gmsh.setParam(getType(), getName(), String.valueOf(_values.get(pos)));
if(_gmsh.onelabCB("check") == 1 && _glView != null)
_glView.requestRender();
}
});
}
else if(_bar != null) {
paramLayout.addView(_bar);
_bar.setEnabled(!_readOnly);
_bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
public void onStopTrackingTouch(SeekBar seekBar) {
_gmsh.setParam(getType(), getName(), String.valueOf(getValue())); // update parameter and the perform a check
if(_gmsh.onelabCB("check") == 1 && _glView != null)
_glView.requestRender();
}
public void onStartTrackingTouch(SeekBar seekBar) {}
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(_listView != null) _listView.refresh();
setValue(getMin() + getStep()*(double)progress);
}
});
}
else if(_checkbox != null) {
paramLayout.removeView(_title);
paramLayout.addView(_checkbox);
_checkbox.setEnabled(!_readOnly);
_checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(_listView != null) _listView.refresh();
setValue((isChecked)? 1 : 0);
_gmsh.setParam(getType(), getName(), String.valueOf(_value));
}
});
}
else if(_edittext != null){
paramLayout.addView(_edittext);
_edittext.setEnabled(!_readOnly);
_edittext.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_ENTER){ // hide the keyboard
InputMethodManager imm = (InputMethodManager)_context.getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(_edittext.getWindowToken(), 0);
_gmsh.setParam(getType(), getName(), String.valueOf(_value));
_edittext.clearFocus();
return true;
}
if(keyCode > KeyEvent.KEYCODE_9 && keyCode != KeyEvent.KEYCODE_NUMPAD_DOT && (keyCode <KeyEvent.KEYCODE_NUMPAD_0 || keyCode >KeyEvent.KEYCODE_NUMPAD_9) && keyCode != KeyEvent.KEYCODE_DEL)
return true;
return false;
}
});
_edittext.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(_listView != null) _listView.refresh();
double value = 1;
try {
if(s.length() < 1) value = 1;
else value = Double.parseDouble(s.toString());
}
catch(NumberFormatException e)
{
value = 1;
//_edittext.setText("");
}
_value = value;
_changed = true;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {} // UNUSED Auto-generated method stub
public void afterTextChanged(Editable s) {} // UNUSED Auto-generated method stub
});
}
return paramLayout;
}
}
package org.geuz.onelab;
import java.util.ArrayList;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
public class ParameterString extends Parameter{
private String _kind;
private int _index;
private ArrayList<String> _choices;
private ArrayAdapter<String> _adapter;
private Spinner _spinner;
private EditText _edittext;
public ParameterString(Context context, Gmsh gmsh, mGLSurfaceView glView, String name) {
super(context, gmsh, glView, name);
_choices = new ArrayList<String>();
_choices.add("-"); // Default choice
}
private void createSpinner()
{
if(_spinner != null) return;
_spinner = new Spinner(_context);
_adapter = new ArrayAdapter<String>(_context, android.R.layout.simple_spinner_dropdown_item, _choices);
_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
_spinner.setAdapter(_adapter);
}
protected void update(){
super.update();
if(_spinner != null)
_spinner.setSelection(_index);
else if(_edittext != null && _choices.size() > 0)
_edittext.setText(_choices.get(0));
}
public void setValue(int index) {if(index != _index)_changed = true;_index = index;this.update();}
public void setValue(String value) {
int index = _choices.indexOf(value);
if(index < 0) { // the value is not in the list, add it
this.addChoices(value);
index = _choices.indexOf(value);
}
if(index != _index)_changed = true;
_index = index;this.update();
}
public void setKind(String kind) {_kind = kind;}
public void addChoices(String choice) {
if(_edittext == null && _spinner == null) createSpinner();
for(String c : _choices) // do not add a duplicate value
if(c.equals(choice))return;
if(_choices.get(0).equals("-")) // remove the default choice with the first added choice
_choices.remove(0);
_choices.add(choice);
this.update();
}
public String getValue() {if( _index < 0) return "";return _choices.get(_index);}
public String getKind() {return _kind;}
public int getIndex() {return _index;}
public ArrayList<String> getChoices() {return _choices;}
public int fromString(String s){
int pos = super.fromString(s);
if(pos <= 0) return -1; // error
String[] infos = s.split("\n");
String value = infos[pos++];
setKind(infos[pos++]); // generic file
if(_kind.equals("file"))
return -1;
int nChoices = Integer.parseInt(infos[pos++]);
if(nChoices < 1 && _kind.equals("generic"))
_edittext = new EditText(_context);
for(int i=0;i<nChoices;i++) this.addChoices(infos[pos++]);
// ...
setValue(value);
this.update();
return pos;
}
public String getType(){return "ParameterString";}
public LinearLayout getView() {
LinearLayout paramLayout = new LinearLayout(_context);
paramLayout.setOrientation(LinearLayout.VERTICAL);
paramLayout.addView(_title);
if(_spinner != null){
paramLayout.addView(_spinner);
_spinner.setEnabled(!_readOnly);
_spinner.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if(_listView != null) _listView.refresh();
}
});
_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onNothingSelected(AdapterView<?> arg0) {}
public void onItemSelected(AdapterView<?> parent, View view,
int pos, long id) {
if(_listView != null) _listView.refresh();
setValue(pos);
_gmsh.setParam(getType(), getName(), String.valueOf(getValue()));
if(_gmsh.onelabCB("check") == 1 && _glView != null)
_glView.requestRender();
}
});
}
else if(_edittext != null){
paramLayout.addView(_edittext);
_edittext.setEnabled(!_readOnly);
_edittext.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_ENTER){ // hide the keyboard
InputMethodManager imm = (InputMethodManager)_context.getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(_edittext.getWindowToken(), 0);
_edittext.clearFocus();
return true;
}
return false;
}
});
_edittext.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(_listView != null) _listView.refresh();
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {} // UNUSED Auto-generated method stub
public void afterTextChanged(Editable s) {
_gmsh.setParam(getType(), getName(), _choices.get(0));
}
});
}
return paramLayout;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment