{ ============================================================================= }
{ unité contenant la description des variables utilisées dans SimulReflec       }
{ ============================================================================= }
{ 24.02.02 : FO : création de l'unité                                           }
{ 27.02.02 : FO : creation de la méthode Update(var MyParam:TExperimentalParameters); }
{                 pour TExperimentalParametersGrid                                    }
{ 02.03.02 : FO : sauvegardes des fichiers de ML et de param                          }
{ 18.04.02 : FO : procedures de copy et de collage dans le clipboard pour TDataReflectivity }
{ 27.05.02 : FO : convolution : les pas de la convolution dépend du nomlbre de points }
{                 discrétisaqtion très empirique : case dlNbr of                      }
{                 1:dQz:=0; 3:dQz:=dQz/2; 5:dQz:=dQz/2; 7:dQz:=dQz/3; 9:dQz:=dQz/3;   }
{ 27.05.02 : FO : corrcetion de generateResolutionSpectrumFixedWL               }
{                 la résolution dQz étaient "variable" avec nbrPoints !!!       }
{                 il ne faut pas aller trop loin dans la gaussienne sinon on smoothe tout }
{ 30.05.02 : FO : * ajout d'un variable FWHM fraction qui détermine le pas a utiliser     }
{                   lorsqu'on génère une gaussienne de résolution               }
{ 13.06.02 : FO : * effacement des data avant le chrgement d'une table Excel    }
{ 16.06.02 : FO : * conversion des data en SI lors de la lecture d'un fichier de data }
{ 26.06.02 : FO : * rajout de nouveaux champs sauvegardés pour ExperimentalParameters }
{                   thicknessFunction, UseThicknessFunction, roughnessFunction, UseRoughnessFunction }
{ 04.07.02 : FO : * ajout d'un champ weighting dans le type DataReflectivity    }
{ 30.07.02 : FO : * nouvelle procedure Clone pour faire une copie d'une ML      }
{                 * changement majeur des paramètres zYN etc                    }
{                   ils peuvent prendre des valeurs -1, 0, 1,2,3 etc            }
{ 20.08.02 : FO : * mise a jour de "generateResolutionSpectrumToF"              }
{                 * nouvelle procedure GenerateQzFastTOF                        }
{ 03.02.03 : FO : * la radioBox 'ABSCISSESUNITS' est correctement mise a jour   }
{                   quand on lit un fichier en nm-1                             }
{ 20.08.03 : FO : * mise a jour des paramètres xCorr dans tous les cas          }
{                   il n'y a plus de valeur "flottante"                         }
{ 22.06.05 : FO : * rajout de nouveaux champs sauvegardés pour ExperimentalParameters }
{                   roughnessFunction, UseRoughnessFunction                     }
{ ============================================================================= }
{ A FAIRE                                                                       }
{ * ne pas créer tous les vecteurs par défaut dans le type TDataReflectivity    }
{   ca consomme pas mal de mémoire                                              }
{ * il faut sortir à terme les vecteurs QzSpectrum et QzIntensity du type DataReflec }
{   et utiliser ExperimentalParameters BOF !                                    }
{ * désactiver le raneg checcking $R+ pour gagner en vitesse                    }
{ * QzSPectrum est d'une taille monumentale !!! OUI !                           }
{ ============================================================================= }
{ ************ problème sérieux avec la résolution   ***********************    }
{ une gaussienne a des pattes très étendues; ca smoothe terriblement les courbes}
{ il faut que le spectre expérimental décroisse plus vite qu'une gaussienne     }
{ => introduire une largeur de cut-off                                          }
{ sinon problème sérieux avec le spectro                                        }
{ il faut fitter le spectre expérimental en longueur d'onde                     }
{ retrouver les courbes utilisées auparavant                                    }
{ RESOLUTION
{    lambdaSpectrum,lambdaIntensity:Tvec; { distribution en longueur d'ondes en m       }
{    thetaSpectrum,thetaIntensity:Tvec;   { distribution en angles d'incidences en rad  }
{    QzIntensity:Tvec;         { distribution expérimentale convertie en Qz  }
{    QzSpectrum:resolutionVectors;                                              }
{    convolution:string;
{    convolution='full theta' or 'full lambda' or 'full theta & lambda' or 'gaussian'   }
{ "full" correspond à l'utilisation d'une convolution en lambda et theta séparée        }
{        cela permet de prendre en compte les vraies formes des résolutions             }
{ "gaussian" on fait une approximation gaussienne (dq/q)² = (dl/l)² + (dt/t)²           }
{        on convolue la courbe calculée avec une gaussienne de largeur qd               }
{        avantage : c'est beaucoup plus rapide lors des calculs (facteur 5 en gros)     }
{        si on considère que 5 points (en lmabda et en theta) dans la convolution sont suffisants }
{ NOTA : pour les fichiers de spectre pour la convolution, il FAUT prendre des points equidistants pour le moment }
unit DataReflec;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, MtxVec, Grids, BaseGrid, AdvGrid, Menus, AdvCGrid, constants, math387, ndspl,
  StdCtrls, ComCtrls, clipBrd, EditReal, Parser10;

const
     VersionNr = 1.5;  { numero de la version du programme; }
                        { cette variable est indispensable pour assurer la compatibilité des fichiers de fit }
     colUnit = 8;
     NbrLayersMax = 99; { mettre au moins 99 pour le cas des ML }
     NbrPointsMax = 5000; { nombre de points maximum dans une courbe de réflectivité }
     NbrProfilesMax = 3;  { nbr max de profiles que l'on peut creer }
     NbrConvMax = 99;      { nombre de points de convolution max pour tenir compte de la résolution }

     { constantes pour le tableau de descripion du systeme multicouche }
     ColYN = 1;          {indice de colonne des différents éléments d'optique }
     ColDescription = 2; { description de la couche, pour memoire }
     ColEpaisseur = 3;ColEpaisseurYN = 4; { epaisseur en nm }
     ColDensity = 5;ColDensityYN = 6;     { densité en 10E28 /m^3 }
     ColB = 7;      ColBYN = 8;     { b en fm }
     ColF = 7;      ColFYN = 8;     { b en fm }
     ColBi = 9;     ColBiYN = 10;   { b en fm }
     ColFi = 9;     ColFiYN = 10;   { b en fm }
     ColM = 11;     ColMYN = 12;    { aimantation en µB par maille }
     ColTheta = 13; ColThetaYN = 14;{ angle polaire de l'aimantation }
     ColPhi = 15;   ColPhiYN = 16;  { angle de l'aimantation % à Ox }
     ColSigma = 17; ColSigYN = 18;
     colXsi = 19;   ColXsiYN = 20;
     Colh = 21;     ColhYN = 22;
     ColMLYN = 23;       { cette couche fait-elle partie de la multicouche }
     ColProfile = 24;
{     ColProfPond = 24; ColProfType = 25;  ColProfNr = 26;       }

     ColPYN = 1;
     ColPDescription = 2;
     ColPLayerNr = 3;
     ColPParameter = 4;
     ColPProfileType = 5;
     ColPBottom = 6;
     ColPTop = 7;
     ColPNrOfSublayers = 8;
     ColPProfPara = 9;

{ indices des différentes séries des graphes }
      SerieRexpUP = 1;  { ref. expérimentale }
      SerieRexpDO = 2; { réf. up exp. }
      SerieRexpSF = 3;
      SerieRexpSFUD = 3; SerieRexpSFDU = 4;
      SerieRcalcUP = 5;  { ref. théorique }
      SerieRcalcDO = 6; { réf. up th. }
      SerieRcalcSF = 7;
      SerieRcalcSFUD = 7; SerieRcalcSFDU = 8;

      SerieDiff1 = 7;
      SerieDiff2 = 8;
      SerieDiff3 = 9;
      SerieDiff4 = 10;

      Store1 = 9; Store2 = 10 ; Store3 = 11 ;Store4 =12;

      SeriePhi1 = 1;SeriePhi2 = 2;SeriePhi3 = 3;SeriePhi4 = 4;
      SeriePhaseUP = 1;SeriePhaseDO = 2;SeriePhaseSF = 3;SeriePhaseSFUD = 3;SeriePhaseSFDU = 4;
      SerieCOSPHI = 1;SerieSin2PHI = 2;
      SerieLambda = 1; { ?!? }
      SeriePhaseRCalcTD = 1; SeriePhaseTCalcTD = 2; SeriePhaseCalcTD = 3;
      SerieDiffus = 1; SerieDiffusU = 1; SerieDiffusD = 2;
      StorePhi1 = 5;StorePhi2 = 6;

      SeriePara1 = 1; { index des graph donnant l'influence des paramètres }
      SeriePara2 = 2;

type
    ResolutionVectors = array[0..NbrConvMax-1] of Tvec;

{ ============================================================================= }
{ description des conditions expérimentales }
TExperimentalParameters = class(Tobject)
  private
  protected
  public
    name:string; { name of this set of parameters }
    { généralités }
    calculatedDataComment,AbscissesUnits:string; { 'deg' ou 'nm-1' }
    { experimental parameters }
    PolarisationType:string;
    ToF:boolean;
    lambda,dlambda,theta,dtheta:double; { en m et en radians }
        { dlambda, largeur FWHM de la gaussienne de resolution }
        { dlambda = FWHM = 2*sigma                             }
    convolution:string; { type of convolution : "full" or "gaussian" }
    convolutionCalculation:string; { manière de calculer la convolution }
        { "full" : méthode pédestre et lente,
        { "QzGaussian" , ne fonctionne qu'avec des spectres en lamda et theta calculés (gaussiens) }
        { "QzFast" , utilisation d'une interpolation 'subtile' }

    dQzNbr,dLambdaNbr,dThetaNbr:integer; { nombre de points dans la courbe de convolution }
        { on préfère que ce nombre de points soit impair (pour le moment)       }
    QzMult,thetaMult,lambdaMult:double;
        { width multiplier of the resolution function : x min,max := x0 +- M * sig *sqrt (2ln2) }
    FWHMfraction:double; { step used (in units of FWHM) when generating a gaussian }
    particles:string;
    VertSize:double;
    ScanRangeMin,ScanRangeStep,ScanRangeMax:double; { pas d'unités prédéfinies : ou nm-1 ou degrés }
    { FIELD }
    FieldMag,FieldTheta,FieldPhi:double; { champ extérieur appliqué en T et les angles en Radians }
    { MULTILAYERS }
    nbrML:integer;
    thicknessFunction:string; { fonction d'évolution des épaisseurs d'une ML }
    roughnessFunction:string; { fonction d'évolution de la rugosité d'une ML }
    magnetizationFunction:string; { fonction d'évolution de la rugosité d'une ML }
    phiFunction:string;
    UseThicknessFunction:boolean;
    UseRoughnessFunction:boolean;
    UseMagnetizationFunction:boolean;
    UsePhiFunction:boolean;
    { FITTING PARAMETERS }
    NormalisationFactor:double;
    NormalisationFactorStep:double;
    FitNormalisation:boolean;
    NbrIterations,randomness:integer;
    PonderationType:string;
    FitStep:double;
    FitAbscisseMin,FitAbscisseMax:double;
    { DIFFUSE }
    scanType:string;
    ThetaDiffMin,ThetaDiffMax,ThetaDiffStep,DeuxThetaDiffMin,DeuxThetaDiffMax,DeuxThetaDiffStep:double;
    AutoRangeDiff:boolean;
    AbscisseDiffType,FormalismDiffType:string;
    lambdaSpectrum,lambdaIntensity:Tvec; { distribution en longueur d'ondes en m          }
    thetaSpectrum,thetaIntensity:Tvec;   { distribution en angles d'incidences en radians }
{    QzSpectrum,QzIntensity:Tvec;    }     { distribution expérimentale convertie en Qz  }
    { procedures }
    constructor Create;
    procedure UpdateDistributions(var MyLambdaGrid,MyThetaGrid:TAdvStringGrid); { mise a jour des variables de resolution et des vecteurs Qz }
    procedure UpdateGridFromVarExpParam(var MyGrid,MyLambdaGrid,MyThetaGrid:TAdvStringGrid);
    procedure UpdateVarExpParamFromGrid(var MyGrid,MyLambdaGrid,MyThetaGrid:TAdvStringGrid);
{    procedure UpdateFromForm(var MyForm:TSimulReflecWindow); { MAJ des paramètres }
{    procedure GenerateResolutionSpectrum
    procedure ReadFromFileXLS(FileName:string;Sheetname); { sheet name should be parameters }
  end;


{ ============================================================================= }
{ data de reflectivité                                                          }
{ cet objet peut contenir des data experimentales ou bien des data calculées    }
TDataReflectivity = class(Tobject)
  private
    Procedure generateResolutionSpectrumFixedWL(var MyExpParam:TExperimentalParameters);
    Procedure generateResolutionSpectrumToF(var MyExpParam:TExperimentalParameters);
    procedure generateResolutionSpectrum(var MyExpParam:TExperimentalParameters);
  protected
  public
    name:string;    { name of this set of data }
    particles:string;        { particules utilisées }
    TimeOfFlight:boolean;    { data mesurées en temps de vol } { NON INITIALISE }{ probablement optionnel }
    PolarisationType:string; { type de polarisation }
    weighting:boolean;       { y a -til une pondération disponible ou pas sur les points? }
    convolution:string;      { type de convolution  }
    convolutionCalculation:string;
    AbscissesUnits:string;
    angles:Tvec;    { vecteur d'angles d'incidence en rad }
    Qz,QzSig:Tvec;        { vecteur des qz en SI } { largeur de la gaussienne pour chaque point Qz }
    dQzNbr:integer;     { nombre de points a utilser dans la convolution }
    QzMult:double;       { width multiplier }
    abscisses:Tvec; { vecteur des abscisses à utiliser pour le graph en deg ou nm-1 }
    lambdaSpectrum,lambdaIntensity:Tvec; { distribution en longueur d'ondes en m      }
    thetaSpectrum,thetaIntensity:Tvec;   { distribution en angles d'incidences en rad }
    QzSpectrum:ResolutionVectors;
    QzIntensity:Tvec; { les poids des points dans les spectres }
    QzFast:Tvec;      { vecteur de Qz qui sert a la convolution rapide }
    IdxInQzFastBot,IdxInQzFastTop:array of integer; { vecteur permettant de ...}
    { vecteur des amplitudes réfléchies et transmises (complexes) }
    R,Rup,Rdo,T,Tup,Tdo,AR,AT:Tvec;       { vecteur des intensités réfléchies et transmises }
    Rs,RupS,RdoS:Tvec;                    { erreur statistique sigma sur les data up et down      }
    Ruu,Rdd,Rdu,Rud,Tuu,Tdd,Tdu,Tud:Tvec; { vecteurs des amplitudes reflechies et transmises }
    RuuS,RddS,RudS,RduS:Tvec;             { ecart quadratique moyen des data                 }
    { procedures }
    constructor Create;
    procedure GenerateQzFastFixedWL(var MyExpParam:TExperimentalParameters);
    procedure GenerateQzFastTOF(var MyExpParam:TExperimentalParameters);
    procedure GenerateQzFast(var MyExpParam:TExperimentalParameters);
    procedure generateAbscisses(var MyExpParam:TExperimentalParameters);
    procedure generateQzAndAnglesAbscisses(var MyExpParam:TExperimentalParameters);
{    procedure Update(var MyExpParam:TExperimentalParameters); { OBSOLETE le 01.04.04  }
    procedure UpdateWithAbscisses(var MyExpParam:TExperimentalParameters);
    procedure UpdateWithRange(var MyExpParam:TExperimentalParameters);
    procedure ReadFromFile(var MyExpCond:TExperimentalParameters;Filename:string);
    procedure ReadFromClipboard(var MyExpCond:TExperimentalParameters);
    procedure SaveToFile(Filename:string);
    procedure SaveToClipboard;
    procedure Normalise(normfact:double);
    procedure Crop(abscMin,AbscMax:double);
    procedure Clone(MyD:TDataReflectivity);
  end;

{ ============================================================================= }
{ objet décrivant un système multicouche }
TProfiles = class(Tobject)
  private
  protected
  public
    NrOfProfiles:integer; { nombre total de profils , en principe au maximum NbrProfilesMax }
    YN:array[1..NbrProfilesMax] of boolean; { veut-on ou non utiliser ce profil ? }
    description:array[1..NbrProfilesMax] of string; { description du profil }
    LayerNr:array[1..NbrProfilesMax] of integer; { layer concernée par le profiling }
    Parameter:array[1..NbrProfilesMax] of string; { paramètre concerné par le profiling }
        { z, rho, bn, bni, f, fi, M, theta, phi }
    profileType:array[1..NbrProfilesMax] of string; { description du profil }
        { smooth, linear, gaussian, erf }
    NrOfSubLayers:array[1..NbrProfilesMax] of integer; { nombre de sous couches }
    bottom:array[1..NbrProfilesMax] of double; { valeur du para coté substrat }
    topp:array[1..NbrProfilesMax] of double; { valeur du para coté vide }
    ProfPara:array[1..NbrProfilesMax] of double; { paramètre caractéristique du profil }
        { for smooth -> weight          }
        { for linear -> no para         }
        { for gaussian -> sigma         }
    ProfiledLayers:set of byte;  { cette variable est-elle bien utile?? }
    constructor Create;
    procedure update(ProfilesGrid:TAdvStringGrid);
    { Nota : pour le moment on garde les variables Top et Bottom en valeur Brute (pas en SI) }
    { ca pose des problemes pour l'aimantation par exemple }
  end;

{ ============================================================================= }
{ objet décrivant un système multicouche }
TMultiLayer = class(Tobject)
  private
  protected
  public
    nbrLayers:integer; { nombre total de couches, substrat et milieu d'incidence compris (le substrat est indexé "0", le vide "nbrLayers-1"}
    nbrParam:integer; { nombre de paramètres a ajuster }
    description:array[0..nbrLayersMax] of string; { description des couches individuelles }
    z,d,bn,bni,f,fi,Aim,theta,phi,sigma,xsi,exph:Tvec;
    BeffMag, BeffTheta, BeffPhi:Tvec;
    V,Vi,Vup,Vdo:Tvec;
    zYN,dYN,bYN,biYN,fYN,fiYN,MYN,thetaYN,PhiYN,sigYN,MLYN:array[0..nbrLayersMax] of integer; { vecteurs indiquant si les paramètres doivent être fittés ou non }
    { -1 = pas de fit; 0 paramètre à fitter mais sans profil; 1,2,3... numéro du profil auquel appartient le paramètre a fitter }
    zStep,dStep,bStep,biStep,fStep,fiStep,AimStep,thetaStep,PhiStep,sigmaStep,MLStep:array[0..nbrLayersMax] of double;
    { vecteurs indiquant les pas pour le fit de type crawling }
    zCorr,dCorr,bCorr,biCorr,fCorr,fiCorr,Mcorr,thetaCorr,phiCorr,sigmaCorr:array[0..nbrLayersMax] of char;
    { vecteurs indiquant quels sont les paramètres corrélés }
    { paramètres corrélés, '0' pas de corrélation, 'A' corrélation 1, 'B' corrélation 2, etc... on se limite à 'F' i.e. 6 corrélations max }
{    zMDS,dMDS,bMDS,biMDS,fMDS,fiMDS,MMDS,thetaMDS,phiMDS,sigmaMDS:array[0..nbrLayersMax] of char;    }
    { vecteurs indiquants quels sont les paramètres libres dans un fit MDS }
    { on va utiliser les Xcorr plutot }
    { '0', paramètres à fitter de manière corrélée entre les couches (pas libre), 'L' comme 'L'ibre indique un paramètre libre entre les différents datasets }
    { paramètres pour les multicouches }
    MLSystem:boolean; {flag indiquant s'il s'agit d'un systeme multicouche }
    zML,dML,bnML,bniML,fML,fiML,AimML,thetaML,phiML,sigmaML,xsiML,exphML:Tvec; { tabelaux de variables spécifique dans le cas d'une ML périodique }
    VML,ViML,VupML,VdoML:Tvec;
    BeffMagML,BeffThetaML,BeffPhiML:Tvec;       { champ effectif dans chaque couche }
    layerIdx:array[0..nbrLayersMax] of integer; { LayerIdx[i] = index de la ligne du tableau qui definit la couche i }
    profile:array[0..nbrLayersMax] of byte;     { flag indiquant à quel profil appartient la couche }
                                                { 0 -> pas de profil attaché                        }
    profiled:boolean; { flag indiquant si cette multicouche est profilées ou pas }
    { procedures }
    { MAJ des paramètres a artir d'une grille }
    constructor Create;
    procedure Clone(MyML:TmultiLayer);
    procedure UpdatePotential(MyEP:TExperimentalParameters);    
    Procedure GenerateMLParameters(var MyExpPar:TExperimentalParameters);
    procedure UpdateFromMultiLayerGrid(var MyGrid:TAdvStringGrid;var MyExperimentalParameters:TExperimentalParameters);
    function SmoothProfile(Var MyProfiles:TProfiles):double;
    Procedure SetCorrParams(var CorrNr:char;para:string;valeur:double);
    { indiquer le format du fichier }
{    ReadFromFileXLS(FileName:string;Sheetname)
    SaveToFile
    CopyToClipBoard }
  end;


{ ============================================================================= }
{ fenetre graphique de la fiche DataReflec }
type
  TdataReflecForm = class(TForm)
    MainMenu1: TMainMenu;
    OpenDialogFitFile: TOpenDialog;
    SaveDialogFitFile: TSaveDialog;
    SaveDialogHTML: TSaveDialog;
    ParserRoughness: TParser;
    ParserThickness: TParser;
    ParserMagnetization: TParser;
    ParserPhi: TParser;

    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure InitDefaultGrid(MyGrid:TAdvStringGrid;Particles:String); { obsolete }
  end;

var
  dataReflecForm: TdataReflecForm;

{ ces variables globales doivent progressivement disparaitrent }
var { les definitions de variables sont passées en global à cause de problèmes avec les fits }
    SourceDirectory:string; { directory ou sont stockés les fichiers d'aide et wav }
    {paramètres pour les multicouches}
    stopFittingFlag:boolean;
    replotFlag:boolean;    
    StopDiffFlag:boolean;
    xsiZ, critereIntegral:double; { critere integral pour les calculs de diffus }

implementation

{uses SimulReflecForm; }

{$R *.dfm}
{$R+}

{ ============================================================================= }
{ méthodes associées à la classe TDataReflectivity                              }
{ ============================================================================= }
constructor TDataReflectivity.Create;
var i:integer;
begin
   inherited Create;
   createIt(angles,Qz,QzSig,Abscisses);
   createIt(LambdaSpectrum,LambdaIntensity);
   createIt(ThetaSpectrum,ThetaIntensity);
   createIt(QzIntensity);createIt(QzFast,QzSig);
   { variables auxiliaires pour le calcul de la reflectivite }
   for i:=0 to NbrConvMax-1 do createIt(QzSpectrum[i]);
   { initialisation des tableaux de data de réflectivité }
   createIt(R,T);createIt(Rs);
   createIt(Rup,Rdo,Tup,Tdo);createIt(RupS,RdoS);
   createIt(Ruu,Rdd,Rud,Rdu);createIt(Tuu,Tdd,Tud,Tdu);
   createIt(RuuS,RddS,RudS,RduS);
   createIt(AR,AT);
end;

{ creation d'un vecteur d'abscisses en Qz pour les calculs des courbes de rflectivité }
{ INPUT : un jeu de paramètres experimentaux, en particulier les valeurs de "scan range" }
{         et lambda                                                                   }
{ OUTPUT : la procedure remplit les deux vecteurs Qz et angles                        }
{ cette procedure utilise des paramètres d'abscisses qu'elle calcules                 }
{ elle ne prend pas en compte les VRAIES abscisses }
procedure TDataReflectivity.generateAbscisses(var MyExpParam:TExperimentalParameters);
var  i,nbrSteps:integer;
     a:double;
begin
   nbrSteps:=floor((MyExpParam.ScanRangeMax-MyExpParam.ScanRangeMin)/MyExpParam.ScanRangeStep);
   angles.size(nbrSteps+1,false);Qz.size(nbrSteps+1,false);Abscisses.size(nbrSteps+1,false);
   a:=MyExpParam.ScanRangeMin;
   for i:=0 to nbrSteps do
   begin { remplissage des abscisses }
        if MyExpParam.AbscissesUnits='deg'
        then begin Angles.values[i]:=a*pi/180;Abscisses.values[i]:=a;
                   Qz.values[i]:=4*pi/MyExpParam.lambda*sin(a*pi/180);
                   { vecteurs dans les unités nm-1 pour une utilisation dans les graph }
                   end;
        if MyExpParam.AbscissesUnits='nm-1'
        then begin Qz.values[i]:=a*1e9;Abscisses.values[i]:=a;
                   Angles.values[i]:=arcsin(a*1e9*MyExpParam.lambda/4/pi);
                   end;
        a:=a+MyExpParam.ScanRangeStep;
   end;
end;

{ creation d'un vecteur d'abscisses en Qz pour les calculs des courbes de rflectivité }
{ INPUT : un jeu de paramètres experimentaux, en particulier les valeurs de "scan range" }
{         et lambda                                                                   }
{ OUTPUT : la procedure remplit les deux vecteurs Qz et angles                        }
{ cette procedure est utile pour les fits avec data expériemenatles                     }
{ la procedure ci-desuus ne fonctionne que pour des calculs des data et ne prend ps en compte les VRAIES absciees }
procedure TDataReflectivity.generateQzAndAnglesAbscisses(var MyExpParam:TExperimentalParameters);
var  i,nbrSteps:integer;
     a:double;
begin
   nbrSteps:=abscisses.length-1;
   angles.size(nbrSteps+1,false);Qz.size(nbrSteps+1,false);
   for i:=0 to nbrSteps do
   begin { remplissage des abscisses }
        a:=Abscisses.values[i];
        if MyExpParam.AbscissesUnits='deg'
        then begin Angles.values[i]:=a*pi/180;
                   Qz.values[i]:=4*pi/MyExpParam.lambda*sin(a*pi/180);
                   { vecteurs dans les unités nm-1 pour une utilisation dans les graph }
                   end;
        if MyExpParam.AbscissesUnits='nm-1'
        then begin Qz.values[i]:=a*1e9;
                   Angles.values[i]:=arcsin(a*1e9*MyExpParam.lambda/4/pi);
                   end;
   end;
end;


{ ********* convolutions ********** }
{ génération d'un spectre en Qz pour chaque point Qz                                   }
{ pour le moment on se limite à des distributions gaussiennes                          }
{ la largeur de la distribution est déterminée par les Dlambda et Dtheta donnés par l'utilisateur }
{ on calcule dQ = Q sqrt((dL/L)² + (dT/T)²)                                            }
{ puis on génére une gaussienne de largeur dQ avec un nombre de points egal            }
{ au maximum de points demandés pour une des distributions lambda ou theta             }
{ INPUT : lambda : longueur d'onde centrale                                            }
{         Qz : les coordonnées de base pour le spectre                                 }
{         implicites : les deux distributions en lambda et theta                       }
{ OUTPUT : QzR : le spectre avec sa resolution pour chaque point                       }
Procedure TDataReflectivity.generateResolutionSpectrumFixedWL(var MyExpParam:TExperimentalParameters);
var i,j:integer;
    dQNbr:integer;
    sig,q,theta0,dq,dQz:double; { angle d'incidence }
    TotalIntensity:double;
begin
   ThetaSpectrum.copy(MyExpParam.thetaSpectrum);ThetaIntensity.copy(MyExpParam.thetaIntensity);
   LambdaSpectrum.copy(MyExpParam.LambdaSpectrum);LambdaIntensity.copy(MyExpParam.LambdaIntensity);
   { generation d'un spectre en Qz pour des data mesurées en Fixed WL }
   dQzNbr:=MyExpParam.dQzNbr;QzMult:=MyExpParam.QzMult;
   QzSig.size(Qz);
   dQNbr:=trunc(MyExpParam.dQzNbr/2);
   QzIntensity.size(MyExpParam.dQzNbr); { spectre unique pour tous les points }
   { definition des tailles des differents spectres }
   for i:=0 to NbrConvMax-1 do QzSpectrum[i].size(Qz.length);
   for i:=0 to Qz.length-1 do
   begin
        { angle d'incidence }
        theta0:=angles.values[i]; { il faut que les angles existent !!! }
        dQz:=Qz.values[i]*sqrt(sqr(MyExpParam.dtheta/theta0)+sqr(MyExpParam.dlambda/MyExpParam.lambda));
{        dQz:=Qz.values[i]*(abs(MyExpParam.dtheta/theta0)+abs(MyExpParam.dlambda/MyExpParam.lambda)); }
        q:=Qz.values[i];sig:=dQz/2;QzSig.values[i]:=sig;
        if dQNbr>0 then dq:=MyExpParam.QzMult*sig*sqrt(2*ln(2))/dQNbr else dq:=0;
        { generation de la gaussienne ayant cette largeur }
        TotalIntensity:=0;
        for j:=-dqNbr to dqNbr do
        begin
           QzSpectrum[j+dqNbr].values[i]:=q+j*dq;
           QzIntensity[j+dqNbr]:=1/(sig*sqrt(2*pi))*exp(-(sqr(j*dq)/(2*sig*sig))); { n'a pas besoin d'etre calculé a chaque fois }
           TotalIntensity:=TotalIntensity+QzIntensity[j+dqNbr];
        end;
        { renormalisation des intensités }
        for j:=-dqNbr to dqNbr do QzIntensity[j+dqNbr]:=QzIntensity[j+dqNbr]/TotalIntensity; { est-ce que ca marche ??? }
        QzIntensity.size(MyExpParam.dQzNbr,false);
   end;
end;

{ génération d'un spectre en Qz pour chaque point Qz                                   }
{ pour le moment on se limite à des distributions gaussiennes                          }
{ la largeur de la distribution est déterminée par les Dlambda et Dtheta donnés par l'utilisateur }
{ on calcule dQ = Q sqrt((dL/L)² + (dT/T)²)                                            }
{ puis on génére une gaussienne de largeur dQ avec un nombre de points egal            }
{ au maximum de points demandés pour une des distributions lambda ou theta             }
{ INPUT : theta : incidence angles (en degrés)                                         }
{         Qz : les coordonnées de base pour le spectre                                 }
{         implicites : les deux distributions en lambda et theta                       }
{ OUTPUT : QzR : le spectre avec sa resolution pour chaque point                       }
Procedure TDataReflectivity.generateResolutionSpectrumToF(var MyExpParam:TExperimentalParameters);
var i,j:integer;
    dQNbr:integer;
    sig,q,lambda0,theta0,dq,dQz:double; { angle d'incidence }
    TotalIntensity:double;
begin
   ThetaSpectrum.copy(MyExpParam.thetaSpectrum);ThetaIntensity.copy(MyExpParam.thetaIntensity);
   LambdaSpectrum.copy(MyExpParam.LambdaSpectrum);LambdaIntensity.copy(MyExpParam.LambdaIntensity);
   { generation d'un spectre en Qz pour des data mesurées en temps de vol }
   dQzNbr:=MyExpParam.dQzNbr;QzMult:=MyExpParam.QzMult;
   QzSig.size(Qz);
   dQNbr:=trunc(MyExpParam.dQzNbr/2);
   QzIntensity.size(MyExpParam.dQzNbr); { spectre unique pour tous les points }
   theta0:=MyExpParam.theta; { angle d'incidence ) { pour le moment il faut un seul angle d'incidence }
   { definition des tailles des differents spectres }
   for i:=0 to NbrConvMax-1 do QzSpectrum[i].size(Qz.length);
   for i:=0 to Qz.length-1 do
   begin
        { longueur d'onde correspondant à ce Qz }
        lambda0:=4*pi/Qz.values[i]*sin(theta0);
        dQz:=Qz.values[i]*sqrt(sqr(MyExpParam.dtheta/theta0)+sqr(MyExpParam.dlambda/lambda0));
        q:=Qz.values[i];sig:=dQz/2;QzSig.values[i]:=sig;
        if dQNbr>0 then dq:=MyExpParam.QzMult*sig*sqrt(2*ln(2))/dQNbr else dq:=0;
        { generation de la gaussienne ayant cette largeur }
        TotalIntensity:=0;
        for j:=-dqNbr to dqNbr do
        begin
           QzSpectrum[j+dqNbr].values[i]:=q+j*dq;
           QzIntensity[j+dqNbr]:=1/(sig*sqrt(2*pi))*exp(-(sqr(j*dq)/(2*sig*sig))); { n'a pas besoin d'etre calculé a chaque fois }
           TotalIntensity:=TotalIntensity+QzIntensity[j+dqNbr];
        end;
        { renormalisation des intensités }
        for j:=-dqNbr to dqNbr do QzIntensity[j+dqNbr]:=QzIntensity[j+dqNbr]/TotalIntensity; { est-ce que ca marche ??? }
        QzIntensity.size(MyExpParam.dQzNbr,false);
   end;
end;

{ generation des spectres de resolution }
{ INPUT : le vecteur de Qz              }
{ OUTPUT : deux vecteurs QzSpectrum et QzIntensity    }
procedure TDataReflectivity.generateResolutionSpectrum(var MyExpParam:TExperimentalParameters);
begin
     if TimeOfFlight=false
     then begin
          Convolution:=MyExpParam.convolution; { probablement mis a jour ailleurs } { A VERIFIER }
          generateResolutionSpectrumFixedWL(MyExpParam);
          end;
     if TimeOfFlight=true
     then begin
          generateResolutionSpectrumToF(MyExpParam);
          end;
end;

{ generation d'un spectre en Qz sur echantillonné pour la convolution rapide en Qz }
procedure TDataReflectivity.GenerateQzFastFixedWL(var MyExpParam:TExperimentalParameters);
var i,j:integer;
    dQNbr:integer;
    sig,q,theta0,dq,dQz,dt,dl:double;
    thetaMax,thetaCrt:double;
    FourPiL:double; { 4*Pi/lambda }
    QzMin,QzMax,QzCrt,sigmaFraction:double;
begin
   { generation d'un spectre en Qz pour des data mesurées en Fixed WL }
   FourPiL:=4*pi/MyExpParam.lambda;
   QzFast.size(NbrPointsMax,false);
   thetaMax:=angles.values[angles.length-1];
{   sigmaFraction:=MyExpParam.QzMult/trunc(MyExpParam.dQzNbr/2); { old version } { has a proble with dQzNbr = 1 }
   sigmaFraction:=MyExpParam.QzMult/MyExpParam.dQzNbr/2;
   { definition des tailles des differents spectres }
   i:=0;
   QzMin:=Qz.values[0]-2*(QzMult+1)*QzSig.values[0]/2*sqrt(2*ln(2));
   QzMax:=Qz.values[Qz.length-1]+2*(QzMult+1)*QzSig.values[Qz.length-1]/2*sqrt(2*ln(2)); { le facteur 2 additionnel est simplement la pour se donner de la marge, sinon il ya encore des problèmes }
   if QzMin<=0 then QzMin:=Qz.values[0];
   thetaCrt:=abs(arcsin(QzMin/FourPiL));
   thetaMax:=abs(arcsin(QzMax/FourPiL));
   QzFast.Values[i]:=FourPiL*sin(thetaCrt); { pour avoir des points dès le premier angle }
   if (MyExpParam.dThetaNbr>1) and (MyExpParam.dLambdaNbr>1) then
   begin { on ne génère le tableau QzFast que s'il y a effectivement une convolution à faire }
   while thetaCrt<=thetamax do
   begin
      { angle d'incidence }
      thetaCrt:=abs(arcsin(QzFast[i]/FourPiL));
      if MyExpParam.dThetaNbr>1 then dt:=MyExpParam.ThetaMult*MyExpParam.dTheta*sqrt(2*ln(2))/(MyExpParam.dThetaNbr-1) else dt:=0;
      if MyExpParam.dLambdaNbr>1 then dl:=MyExpParam.LambdaMult*MyExpParam.dLambda*sqrt(2*ln(2))/(MyExpParam.dLambdaNbr-1) else dl:=0;
      if MyExpParam.dQzNbr>1 then dq:=MyExpParam.QzMult*dQz*sqrt(2*ln(2))/(MyExpParam.dQzNbr-1) else dq:=0;
      { determination du pas en dqz correspondant à ThetaCrt }
      { dQz:=Qz.values[i]*sqrt(sqr(MyExpParam.dtheta/theta0)+sqr(MyExpParam.dlambda/MyExpParam.lambda)); }
      { il ne faut sans doute pas sommer les deux valeurs dt et dl ... }
      { prendre le plus petit des deux est inefficace => il y a trop de points }
      dQz:=QzFast.values[i]*(abs(dt/thetaCrt)+abs(dl/MyExpParam.lambda))*sigmaFraction;
      QzFast.Values[i+1]:=QzFast.Values[i]+dQz;
      inc(i);
   end;
   QzFast.size(i,false); { redimensionnement du spectre }
   { creation d'un tableau donnant les indices dans le tableua QzFast } {IdxInQzFastBot et IdxInQzFastTop }
   i:=0;dqz:=0.001; { en nm-1 } { ce pas correspond a des pas en theta de 0.002° à 0.43nm }
                                { au maximum on va à des 2-3 nm-1 aux neutrons }
   QzCrt:=QzFast.min*1e-9+dqz; { verifier si c'est pertinent }
   setLength(IdxInQzFastBot,NbrPointsMax*10);setLength(IdxInQzFastTop,NbrPointsMax*10);
   IdxInQzFastBot[0]:=0;IdxInQzFastTop[0]:=0;
   while (QzCrt*1e9<QzFast.max) do
   begin
      if (QzFast.Values[i]<=QzCrt*1e9) and (QzCrt*1e9<QzFast.Values[i+1])
      then begin
           IdxInQzFastBot[round((QzCrt-QzFast.Min*1e-9)/dqz)]:=i;IdxInQzFastTop[round((QzCrt-QzFast.Min*1e-9)/dqz)]:=i+1;
           QzCrt:=QzCrt+dQz;
           end
      else begin
           inc(i);
           end;
   end;
   setLength(IdxInQzFastBot,round((QzCrt-dQz)/dqz));setLength(IdxInQzFastTop,round((QzCrt-dQz)/dqz));
   end; { on ne généère le tableau QzFats que s'il y a effectivement une convolution à faire }
end;

{ generation d'un spectre en Qz sur echantillonné pour la convolution rapide en Qz }
procedure TDataReflectivity.GenerateQzFastTOF(var MyExpParam:TExperimentalParameters);
var i,j:integer;
    dQNbr:integer;
    sig,q,lambda0,theta0,dq,dQz,dt,dl:double; { angle d'incidence }
    FourPiL:double; { 4*Pi/lambda }
    QzMin,QzMax,QzCrt,sigmaFraction:double;
begin
   { generation d'un spectre en Qz pour des data mesurées en Fixed WL }
   FourPiL:=4*pi/MyExpParam.lambda;
   QzFast.size(NbrPointsMax,false);
   sigmaFraction:=MyExpParam.QzMult/trunc(MyExpParam.dQzNbr/2);
   { definition des tailles des differents spectres }
   i:=0;theta0:=MyExpParam.theta;
   QzMin:=Qz.values[0]-2*(QzMult+1)*QzSig.values[0]/2*sqrt(2*ln(2));
   QzMax:=Qz.values[Qz.length-1]+2*(QzMult+1)*QzSig.values[Qz.length-1]/2*sqrt(2*ln(2)); { le facteur 2 additionnel est simplement la pour se donner de la marge, sinon il ya encore des problèmes aux "extrémités" }
   if QzMin<=0 then QzMin:=Qz.values[0];
   QzCrt:=QzMin;
   QzFast.Values[i]:=QzCrt; { pour avoir des points dès le premier angle }
   dQz:=0;
   while QzCrt<Qzmax do   { le facteur 5 est arbitraire il sert simplment a avoir de la marge dans QzFast aux grands Q }
   begin
      { longueur d'onde }
      lambda0:=4*pi/QzFast.values[i]*sin(theta0);
      if MyExpParam.dThetaNbr>1 then dt:=MyExpParam.ThetaMult*MyExpParam.dTheta*sqrt(2*ln(2))/(MyExpParam.dThetaNbr-1) else dt:=0;
      if MyExpParam.dLambdaNbr>1 then dl:=MyExpParam.LambdaMult*MyExpParam.dLambda*sqrt(2*ln(2))/(MyExpParam.dLambdaNbr-1) else dl:=0;
      if MyExpParam.dQzNbr>1 then dq:=MyExpParam.QzMult*dQz*sqrt(2*ln(2))/(MyExpParam.dQzNbr-1) else dq:=0;
      { determination du pas en dqz correspondant à ThetaCrt }
      { dQz:=Qz.values[i]*sqrt(sqr(MyExpParam.dtheta/theta0)+sqr(MyExpParam.dlambda/MyExpParam.lambda)); }
      { il ne faut sans doute pas sommer les deux valeurs dt et dl ... }
      { prendre le plus petit des deux est inefficace => il y a trop de points }
      dQz:=QzFast.values[i]*(abs(dt/theta0)+abs(dl/lambda0))*sigmaFraction;
      QzFast.Values[i+1]:=QzFast.Values[i]+dQz;
      QzCrt:=QzFast.Values[i+1];
      inc(i);
   end;
   QzFast.size(i,false); { redimensionnement du spectre }
   { creation d'un tableau donnant les indices dans le tableua QzFast } {IdxInQzFastBot et IdxInQzFastTop }
   i:=0;dqz:=0.001; { en nm-1 } { ce pas correspond a des pas en theta de 0.002° à 0.43nm }
                                { au maximum on va à des 2-3 nm-1 aux neutrons }
   QzCrt:=QzFast.min*1e-9+dqz; { verifier si c'est pertient }
   setLength(IdxInQzFastBot,NbrPointsMax*10);setLength(IdxInQzFastTop,NbrPointsMax*10);
   IdxInQzFastBot[0]:=0;IdxInQzFastTop[0]:=0;
   while (QzCrt*1e9<QzFast.max) do
   begin
      if (QzFast.Values[i]<=QzCrt*1e9) and (QzCrt*1e9<QzFast.Values[i+1])
      then begin
           IdxInQzFastBot[round((QzCrt-QzFast.Min*1e-9)/dqz)]:=i;IdxInQzFastTop[round((QzCrt-QzFast.Min*1e-9)/dqz)]:=i+1;
           QzCrt:=QzCrt+dQz;
           end
      else begin
           inc(i);
           end;
   end;
   setLength(IdxInQzFastBot,round((QzCrt-dQz)/dqz));setLength(IdxInQzFastTop,round((QzCrt-dQz)/dqz));
end;

procedure TDataReflectivity.GenerateQzFast(var MyExpParam:TExperimentalParameters);
begin
   if TimeOfFlight=false
   then begin generateQzFastFixedWL(MyExpParam) end;
   if TimeOfFlight=true
   then begin generateQzFastToF(MyExpParam) end;
end;

{ mise a jour des champs a partir des condirtions experimentales }
{ et generation des spectres experimentaux                      }
{ OBSOLETE le 01.04.04 }
{procedure TDataReflectivity.Update(var MyExpParam:TExperimentalParameters); 
begin
   name:=MyExpParam.CalculatedDataComment;
   Particles:=MyExpParam.Particles;
   PolarisationType:=MyExpParam.PolarisationType;
   TimeOfFlight:=MyExpParam.Tof;
   AbscissesUnits:=MyExpParam.AbscissesUnits;
   Convolution:=MyExpParam.Convolution;
   ConvolutionCalculation:=MyExpParam.ConvolutionCalculation;
   lambdaSpectrum.copy(MyExpParam.lambdaSpectrum);lambdaIntensity.copy(MyExpParam.lambdaIntensity);
   thetaSpectrum.copy(MyExpParam.thetaSpectrum);thetaIntensity.copy(MyExpParam.thetaIntensity);
   generateResolutionSpectrum(MyExpParam);
   GenerateQzFast(MyExpParam);
end; }

{ mise a jour des champs a partir des condirtions experimentales }
{ génération des vecteurs Anges et Qz à partir des data du vecteur ABSCISSES }
{ et generation des spectres experimentaux                      }
procedure TDataReflectivity.UpdateWithAbscisses(var MyExpParam:TExperimentalParameters); { mise a jour des variables de resolution et des vecteurs Qz }
begin
   name:=MyExpParam.CalculatedDataComment;
   Particles:=MyExpParam.Particles;
   PolarisationType:=MyExpParam.PolarisationType;
   TimeOfFlight:=MyExpParam.Tof;
   AbscissesUnits:=MyExpParam.AbscissesUnits;
   Convolution:=MyExpParam.Convolution;
   ConvolutionCalculation:=MyExpParam.ConvolutionCalculation;
   lambdaSpectrum.copy(MyExpParam.lambdaSpectrum);lambdaIntensity.copy(MyExpParam.lambdaIntensity);
   thetaSpectrum.copy(MyExpParam.thetaSpectrum);thetaIntensity.copy(MyExpParam.thetaIntensity);
   generateQzAndAnglesAbscisses(MyExpParam);
   generateResolutionSpectrum(MyExpParam);
   GenerateQzFast(MyExpParam);
end;

{ mise a jour des champs a partir des condirtions experimentales }
{ génération des vecteur Angles et Qz à partir du range de la fenetre graphique }
{ et generation des spectres experimentaux                      }
procedure TDataReflectivity.UpdateWithRange(var MyExpParam:TExperimentalParameters); { mise a jour des variables de resolution et des vecteurs Qz }
begin
   name:=MyExpParam.CalculatedDataComment;
   Particles:=MyExpParam.Particles;
   PolarisationType:=MyExpParam.PolarisationType;
   TimeOfFlight:=MyExpParam.Tof;
   AbscissesUnits:=MyExpParam.AbscissesUnits;
   Convolution:=MyExpParam.Convolution;
   ConvolutionCalculation:=MyExpParam.ConvolutionCalculation;
   lambdaSpectrum.copy(MyExpParam.lambdaSpectrum);lambdaIntensity.copy(MyExpParam.lambdaIntensity);
   thetaSpectrum.copy(MyExpParam.thetaSpectrum);thetaIntensity.copy(MyExpParam.thetaIntensity);
   generateAbscisses(MyExpParam);
   generateResolutionSpectrum(MyExpParam);
   GenerateQzFast(MyExpParam);
end;


{ lecture d'un fichier de data expérimentales                                   }
{ INPUT : MyExperimentalConditions                                              }
{         FileName : le nom complet du fichier                                  }
{ NOTA : après appel de cette procédure il faut remettre a jour les valeur      }
{       du panneau de controle a partir de TExperimentalConditions             }
procedure TDataReflectivity.ReadFromFile(var MyExpCond:TExperimentalParameters;Filename:string);
var
  i,errorcode:integer;
  nbrPointsExp:integer; { nombre de points expérimentaux }
  indiceValeur:integer; { indice de la valeur dans la ligne de texte }
  F: TextFile;
  S,S1,S2,S3: string[255];
  aux:double;
  valeurs:array[1..10] of double; { valeurs numériques lues sur la ligne }
  finChaine,FirstEntrance:boolean; { FirstEntrance est "nécessaire" pour la logique des boucles }
begin
   Angles.size(nbrPointsMax,false);Qz.size(nbrPointsMax,false);Abscisses.size(nbrPointsMax,false);
   R.size(nbrPointsMax,false);Rup.size(nbrPointsMax,false);Rdo.size(nbrPointsMax,false);
   Rs.size(nbrPointsMax,false);RupS.size(nbrPointsMax,false);RdoS.size(nbrPointsMax,false);
   Ruu.size(nbrPointsMax,false);Rdd.size(nbrPointsMax,false);Rud.size(nbrPointsMax,false);Rdu.size(nbrPointsMax,false);
   RuuS.size(nbrPointsMax,false);RddS.size(nbrPointsMax,false);RudS.size(nbrPointsMax,false);RduS.size(nbrPointsMax,false);

   AssignFile(F, FileName);{ Fichier sélectionné dans la boîte de dialogue }
   Reset(F);
   { lecture de l'en-tête }
   readln(F,s);
   while s[1]='#' do
   begin
      s1:='';i:=2; { finChaine:=false; }
        { on saute le tab ou le égal ou l'espace }
        while ((s[i]=#9) or (s[i]=' ')) and (i<=length(s)) do inc(i);
        s1:='';
        while (s[i]<>#9) and (s[i]<>',') and (s[i]<>' ') and (i<=length(s)) do {#9 est un caractère tab , #10 est LF}
           begin s1:=s1+s[i];i:=i+1; end;
        s2 := uppercase(s1); { type de paramètre }
{        if i>length(S) then FinChaine:=true; }
        i:=i+1;
        { on saute le tab ou le égal ou l'espace }
        while ((s[i]=#9) or (s[i]=' ') or (s[i]='=')) and (i<=length(s)) do inc(i);
        { lecture de la valeur du paramètre proprement dit }
        s1:='';
        while (i<=length(s)) do begin s1:=s1+s[i];i:=i+1; end;
        s3:=s1;
      { mise a jour du paramètre à partir de la valeur de l'en-tête }
      if s2='COMMENT' then begin name:=s3;MyExpCond.name:=s3 end;
      if s2='PARTICLES' then begin if (s3='neutrons') or (s3='xrays')
                                   then begin Particles:=s3;MyExpCond.particles:=s3 end
                                   else MessageDlg('Particles type not recognized, choose <neutrons> or <xrays>', mtInformation, [mbOk], 0);
                                   end;
      if s2='POLARISATION' then begin if (s3='non polarised') or (s3='polarised') or (s3='with spin-flip') or (s3='with SF high field')
                                      then begin PolarisationType:=s3;MyExpCond.PolarisationType:=s3 end
                                      else MessageDlg('Polarisation type not recognized, choose <non polarised> or <polarised> or <with spin-flip> or <with SF high field>', mtInformation, [mbOk], 0);
                                end;
      if (s2='ABSCISSESUNITS') or (s2='ABSCISSES') then begin if (s3='deg') or (s3='nm-1')
                                                              then begin AbscissesUnits:=s3;MyExpCond.AbscissesUnits:=s3 end { deg or nm-1 }
                                                              else MessageDlg('Abscisses type not recognized, choose <deg> or <nm-1>', mtInformation, [mbOk], 0);
                                                              end;
      if s2='TIMEOFFLIGHT' then begin s3:=uppercase(s3);
                                      if (s3='FALSE') or (s3='TRUE')
                                      then if s3='FALSE' then begin TimeOfFlight:=false;MyExpCond.ToF:=false end
                                                         else begin TimeOfFlight:=true;MyExpCond.ToF:=true end
                                      else MessageDlg('Time-of-Flight option not recognized, choose <True> or <False>', mtInformation, [mbOk], 0);
                                end;
      readln(F,s);
   end;
   FirstEntrance:=true;
   { lecture des data proprement dites }
    nbrPointsExp:=0;
    while not(Eof(F)) do { lecture des data du fichier }
    begin
       if FirstEntrance=false then Readln(F, S);
       { traitement de la chaine pour en extraire les nombres }
       S1:='';i:=1;finChaine:=false;indiceValeur:=1;
       while not FinChaine do
       begin
           s1:='';
           while (S[i]<>#9) and (S[i]<>',') and (i<=length(S)) do {#9 est un caractère tab , #10 est LF}
             begin
                  s1:=s1+s[i];i:=i+1;
             end;
           if i>length(S) then FinChaine:=true;
           i:=i+1;{ on saute le tab ou la virgule }
           val(s1,aux,ErrorCode);valeurs[indiceValeur]:=aux;
            indiceValeur:=indiceValeur+1;
       end;
       dec(indiceValeur); { ca donne le nombre de colonnes }
        { remplissage des vecteurs en fonction de la situation }
        { non polarisé OU polarisé OU polarisé avec analyse de polarisation }
      if valeurs[1]<>0
      then begin {MessageDlg('There are blank lines in your data file!'
                +#13+'Please clean and reload your file before proceeding further.'
                +#13+'(you will run into problems otherwise)', mtWarning, [mbOk], 0); }
        { remplissage des abscisses }
        Abscisses[nbrPointsExp]:=valeurs[1];
        if AbscissesUnits='deg'
        then begin Angles[nbrPointsExp]:=valeurs[1]*pi/180;
                   Qz[nbrPointsExp]:=4*pi/(MyExpCond.lambda)*sin(valeurs[1]*pi/180); end;
        if AbscissesUnits='nm-1'
        then begin Qz[nbrPointsExp]:=valeurs[1]*1e9;
                   Angles[nbrPointsExp]:=arcsin(valeurs[1]*1e9*MyExpCond.lambda/4/pi);
                   end;
        { remplissage des ordonnées }
        if PolarisationType='non polarised'
        then begin
             R[nbrPointsExp]:=valeurs[2];
             { on teste si la variance figure dans le fichier }
             if indiceValeur >= 3
             then begin Rs[nbrPointsExp]:=valeurs[3];weighting:=true; end
             else begin
                  MyExpCond.ponderationType:='linear'; { pour eviter les accidents }
                  weighting:=false;
{                  WarningMessage.caption:='No Variance Given'; }
                  end;
             end;
        if PolarisationType='polarised'
        then begin
             { on teste si la variance figure dans le fichier }
             if indiceValeur >= 5
             then begin
                  Rup[nbrPointsExp]:=valeurs[2];
                  RupS[nbrPointsExp]:=valeurs[3];
                  Rdo[nbrPointsExp]:=valeurs[4];
                  RdoS[nbrPointsExp]:=valeurs[5];
                  weighting:=true;
                  end
             else begin
                  Rup[nbrPointsExp]:=valeurs[2];
                  Rdo[nbrPointsExp]:=valeurs[3];
                  MyExpCond.ponderationType:='linear'; { pour eviter les accidents }
                  weighting:=false;
{                  WarningMessage.caption:='No Variance Given'; }
                  end;
             end;
        if PolarisationType='with spin-flip'
        then begin
             { on teste si la variance figure dans le fichier }
             if indiceValeur >= 5
             then begin
                  Ruu[nbrPointsExp]:=valeurs[2];RuuS[nbrPointsExp]:=valeurs[3];
                  Rdd[nbrPointsExp]:=valeurs[4];RddS[nbrPointsExp]:=valeurs[5];
                  Rud[nbrPointsExp]:=valeurs[6];RudS[nbrPointsExp]:=valeurs[7];
                  Rdu[nbrPointsExp]:=valeurs[6];RduS[nbrPointsExp]:=valeurs[7];
                  weighting:=true;
                  end
             else begin
                  Ruu[nbrPointsExp]:=valeurs[2];
                  Rdd[nbrPointsExp]:=valeurs[3];
                  Rud[nbrPointsExp]:=valeurs[4];Rdu[nbrPointsExp]:=valeurs[4];
                  MyExpCond.ponderationType:='linear'; { pour eviter les accidents }
                  weighting:=false;
{                  WarningMessage.caption:='No Variance Given'; }
                  end;
             end;
        if PolarisationType='with SF high field'
        then begin
             { on teste si la variance figure dans le fichier }
             if indiceValeur >= 8
             then begin
                  Ruu[nbrPointsExp]:=valeurs[2];RuuS[nbrPointsExp]:=valeurs[3];
                  Rdd[nbrPointsExp]:=valeurs[4];RddS[nbrPointsExp]:=valeurs[5];
                  Rud[nbrPointsExp]:=valeurs[6];RudS[nbrPointsExp]:=valeurs[7];
                  Rdu[nbrPointsExp]:=valeurs[8];RduS[nbrPointsExp]:=valeurs[9];
                  weighting:=true;
                  end
             else begin
                  Ruu[nbrPointsExp]:=valeurs[2];Rdd[nbrPointsExp]:=valeurs[3];
                  Rud[nbrPointsExp]:=valeurs[4];Rdu[nbrPointsExp]:=valeurs[5];
                  MyExpCond.ponderationType:='linear'; { pour eviter les accidents }
                  weighting:=false;
{                  WarningMessage.caption:='No Variance Given'; }
                  end;
             end;
        nbrPointsExp:=nbrPointsExp+1;
        FirstEntrance:=false;
      end;
    end;

    Angles.size(nbrPointsExp,false);Qz.size(nbrPointsExp,false);Abscisses.size(nbrPointsExp,false);
    R.size(nbrPointsExp,false);Rs.size(nbrPointsExp,false);
    Rup.size(nbrPointsExp,false);RupS.size(nbrPointsExp,false);
    Rdo.size(nbrPointsExp,false);RdoS.size(nbrPointsExp,false);
    Ruu.size(nbrPointsExp,false);RuuS.size(nbrPointsExp,false);
    Rdd.size(nbrPointsExp,false);RddS.size(nbrPointsExp,false);
    Rud.size(nbrPointsExp,false);RudS.size(nbrPointsExp,false);
    Rdu.size(nbrPointsExp,false);RduS.size(nbrPointsExp,false);

    CloseFile(F);
end;

{ copy des data expérimentales a partir du clipBoard }
{ INPUT : un clipboard qui contient des data !!!     }
{ OUTPUT : les data sont stockées dans les tableaux  }
{          correspondants                            }
{ NOTA : après appel de cette procédure il faut remettre a jour les valeur      }
{       du panneau de controle a partir de TExperimentalConditions             }
procedure TDataReflectivity.ReadFromClipboard(var MyExpCond:TExperimentalParameters);
var
   ContenuClipboard, ps1,ps2   : PChar;
   dwLen : DWord;
   tf    : TextFile;
   hData : THandle;
   StringGlobalHandle:THandle;
   StringGlobalSize:Longint;
   StringGlobalPtr:PChar;
   i,j,IndiceLigne,IndiceColonne,ErrorPos:integer;
   carac,carac1:char;
   S,S1:String;
   SPchar:array [0..200] of char;
   FinNombre,FinLigneDeNombres:boolean;
   dataExp:array[1..nbrPointsMax,1..9] of double; { tableau intermédiare avant de transferer les data dans les tableaux pertinents }
                                                  { on se limite à 5 colonnes pour le moment }
   longueur:integer;
   nbrColonnes,nbrLignes:integer;
begin
   with Clipboard do
   begin
      try
         Open;
         if( HasFormat( CF_TEXT ) )
         then begin
              hData := GetClipboardData( CF_TEXT );
              ps1 := GlobalLock( hData );
              dwLen := GlobalSize( hData );
              ContenuClipboard := StrAlloc( 1 + dwLen );
              StrLCopy( ContenuClipboard, ps1, dwLen );
              GlobalUnlock( hData );
              end;
      finally
         Close;
      end;
    end;

     { lecture du contenu de la variable ContenuClipBoard }
     { et transcription en tableau de chiffres au format Courbe }
     indiceLigne:=1;
     indiceColonne:=1;
     i:=0;
     longueur:=StrLen(ContenuClipboard)-2;
     while i<=longueur do
     begin
          finLignedeNombres:=False;
          While FinLigneDeNombres=False do
          begin
               S:='';
               FinNombre:=False;
               while finNombre=false do
               begin
                    carac:=ContenuClipboard[i];
                    carac1:=ContenuClipboard[i+1];
                    if (carac=#9)
                    then begin
                         finNombre:=true;
                         val(S,dataExp[IndiceLigne,IndiceColonne],errorpos);
                         i:=i+1;
                         indiceColonne:=indiceColonne+1;
                         end
                    else if (carac=#13) and (carac1=#10)
                         then begin
                              finNombre:=true;
                              finLigneDeNombres:=true;
                              val(S,dataExp[IndiceLigne,IndiceColonne],errorpos);
                              i:=i+2;
                              NbrColonnes:=IndiceColonne;
                              indiceColonne:=1;
                              indiceLigne:=IndiceLigne+1;
                              end
                         else begin
                              S:=S+carac;
                              i:=i+1;
                              end;
               end;
          end;
     end;
     nbrLignes:=IndiceLigne-1;
     { stockage des dataExp dans les vecteurs ad-hoc }
   Angles.size(nbrPointsMax,false);Qz.size(nbrPointsMax,false);Abscisses.size(nbrPointsMax,false);
   R.size(nbrPointsMax,false);Rup.size(nbrPointsMax,false);Rdo.size(nbrPointsMax,false);
   Rs.size(nbrPointsMax,false);RupS.size(nbrPointsMax,false);RdoS.size(nbrPointsMax,false);
   Ruu.size(nbrPointsMax,false);Rdd.size(nbrPointsMax,false);Rud.size(nbrPointsMax,false);Rdu.size(nbrPointsMax,false);
   RuuS.size(nbrPointsMax,false);RddS.size(nbrPointsMax,false);RudS.size(nbrPointsMax,false);RduS.size(nbrPointsMax,false);

   { mise a jour des paramètres du DataReflec }
     for i:=0 to nbrLignes-1 do
     begin
        { remplissage des abscisses }
        Abscisses[i]:=DataExp[i+1,1];
        if AbscissesUnits='deg'
        then begin Angles[i]:=DataExp[i+1,1];
                   Qz[i]:=4*pi/(MyExpCond.lambda)*sin(DataExp[i+1,1]*pi/180); end;
        if AbscissesUnits='nm-1'
        then begin Qz[i]:=DataExp[i+1,1]*1e9;
                   Angles[i]:=arcsin(DataExp[i+1,1]*MyExpCond.lambda/4/pi);
                   end;
        { remplissage des ordonnées }
        if PolarisationType='non polarised'
        then begin
             R[i]:=DataExp[i+1,2];
             { on teste si la variance figure dans le fichier }
             if nbrColonnes >= 3
             then Rs[i]:=DataExp[i+1,3]
             else begin
                  MyExpCond.ponderationType:='linear'; { pour eviter les accidents }
                  { WarningMessage.caption:='No Variance Given'; }
                  end;
             end;
        if PolarisationType='polarised'
        then begin
             { on teste si la variance figure dans le fichier }
             if nbrColonnes >= 5
             then begin
                      Rup[i]:=DataExp[i+1,2];
                      RupS[i]:=DataExp[i+1,3];
                      Rdo[i]:=DataExp[i+1,4];
                      RdoS[i]:=DataExp[i+1,5];
                  end
             else begin
                      Rup[i]:=DataExp[i+1,2];
                      Rdo[i]:=DataExp[i+1,3];
                      MyExpCond.ponderationType:='linear'; { pour eviter les accidents }
{                  WarningMessage.caption:='No Variance Given'; }
                  end;
             end;
        if PolarisationType='with spin-flip'
        then begin
             { on teste si la variance figure dans le fichier }
             if nbrColonnes >= 5
             then begin
                  Ruu[i]:=DataExp[i+1,2];RuuS[i]:=DataExp[i+1,3];;
                  Rdd[i]:=DataExp[i+1,4];RddS[i]:=DataExp[i+1,5];;
                  Rud[i]:=DataExp[i+1,6];RudS[i]:=DataExp[i+1,7];;
                  Rdu[i]:=DataExp[i+1,6];RduS[i]:=DataExp[i+1,7];;
                  end
             else begin
                  Ruu[i]:=DataExp[i+1,2];;
                  Rdd[i]:=DataExp[i+1,3];;
                  Rud[i]:=DataExp[i+1,4];;Rdu[i]:=DataExp[i+1,4];;
                  MyExpCond.ponderationType:='linear'; { pour eviter les accidents }
{                  WarningMessage.caption:='No Variance Given'; }
                  end;
             end;
        if PolarisationType='with SF high field'
        then begin
             { on teste si la variance figure dans le fichier }
             if nbrColonnes >= 8
             then begin
                  Ruu[i]:=DataExp[i+1,2];RuuS[i]:=DataExp[i+1,3];
                  Rdd[i]:=DataExp[i+1,4];RddS[i]:=DataExp[i+1,5];
                  Rud[i]:=DataExp[i+1,6];RudS[i]:=DataExp[i+1,7];
                  Rdu[i]:=DataExp[i+1,8];RduS[i]:=DataExp[i+1,9];
                  end
             else begin
                  Ruu[i]:=DataExp[i+1,2];Rdd[i]:=DataExp[i+1,3];
                  Rud[i]:=DataExp[i+1,4];Rdu[i]:=DataExp[i+1,5];
                  MyExpCond.ponderationType:='linear'; { pour eviter les accidents }
{                  WarningMessage.caption:='No Variance Given'; }
                  end;
             end;
    end;

    Angles.size(nbrLignes,false);Qz.size(nbrLignes,false);Abscisses.size(nbrLignes,false);
    R.size(nbrLignes,false);Rs.size(nbrLignes,false);
    Rup.size(nbrLignes,false);RupS.size(nbrLignes,false);
    Rdo.size(nbrLignes,false);RdoS.size(nbrLignes,false);
    Ruu.size(nbrLignes,false);RuuS.size(nbrLignes,false);
    Rdd.size(nbrLignes,false);RddS.size(nbrLignes,false);
    Rud.size(nbrLignes,false);RudS.size(nbrLignes,false);
    Rdu.size(nbrLignes,false);RduS.size(nbrLignes,false);
end;


{ sauvegarde d'un fichier d'un spectre TDataReflectivity (calculé ou simulé) }
procedure TDataReflectivity.SaveToFile(Filename:string); { mise a jour des variables de resolution et des vecteurs Qz }
var
  i:integer;
  F: TextFile;
  S: string[255];
begin
    AssignFile(F, FileName);{ Fichier sélectionné dans la boîte de dialogue }
    Rewrite(F);
    { sauvegarde de quelques informations de base sur les data qui viennent d'tre calculées }
    writeln(F,'# Comment = '+name); { un petit commentaire }
    writeln(F,'# Particles = '+particles);
    writeln(F,'# Polarisation = '+PolarisationType);
    writeln(F,'# Abscisses = '+AbscissesUnits);
    if TimeOfFlight=true then s:='true' else s:='false';
    writeln(F,'# TimeOfFlight = '+s);
    for i:=0 to Abscisses.length-1 do
    begin
         s:=floattostr(abscisses.values[i]);write(F,s);write(F,chr(9));
         if PolarisationType='non polarised'
         then begin
                s:=floattostr(R.values[i]);write(F,s);writeln(F);
              end;
         if PolarisationType='polarised'
         then begin
                s:=floattostr(Rup.values[i]);write(F,s);write(F,chr(9));
                s:=floattostr(Rdo.values[i]);write(F,s);writeln(F);
              end;
         if PolarisationType='with spin-flip'
         then begin
                s:=floattostr(Ruu.values[i]);write(F,s);write(F,chr(9));
                s:=floattostr(Rdd.values[i]);write(F,s);write(F,chr(9));
                s:=floattostr(Rud.values[i]);write(F,s);writeln(F);
              end;
         if PolarisationType='with SF high field'
         then begin
                s:=floattostr(Ruu.values[i]);write(F,s);write(F,chr(9));
                s:=floattostr(Rdd.values[i]);write(F,s);write(F,chr(9));
                s:=floattostr(Rud.values[i]);write(F,s);write(F,chr(9));
                s:=floattostr(Rdu.values[i]);write(F,s);writeln(F);
              end;
    end;
    CloseFile(F);
end;

{ copy des data expérimentales dans le clipBoard     }
{ INPUT : les courbes Rcalc                          }
{ OUTPUT : les data sont copiées dans le clipboard   }
{ REM : il faut des data dans R ou Rup Rdo !!         }
{ L'implementation est faire a l'ancienne pour le clipboard (type BPW) }
{ parce que clipBoard n'est pas documenté dans Delphi !?               }
procedure TDataReflectivity.SaveToClipboard;
var
   StringGlobalHandle:THandle;
   StringGlobalPtr:PChar;
   i,j:integer;
   S,S1:String;
   SPchar:array [0..200] of char;
   ContenuClipBoard:PChar;
   nbrPoints:integer;
begin
     ContenuClipboard := StrAlloc(65534);
     StrPCopy(SPChar,'');
     StrCopy(ContenuClipboard,SPchar);
     nbrPoints:=Abscisses.length;
     for i:=0 to nbrPoints-1 do
     begin
         S:='';
         str(Abscisses[i]:11:9,S1);S:=S+S1+#9;
         if PolarisationType='non polarised'
         then begin
              str(R[i]:11:9,S1);S:=S+S1+#13+#10;
              end;
         if PolarisationType='polarised'
         then begin
              str(Rup[i]:11:9,S1);S:=S+S1+#9;
              str(Rdo[i]:11:9,S1);S:=S+S1+#13+#10;
              end;
         if PolarisationType='with spin-flip'
         then begin
              str(Ruu[i]:11:9,S1);S:=S+S1+#9;
              str(Rdd[i]:11:9,S1);S:=S+S1+#9;
              str(Rud[i]:11:9,S1);S:=S+S1+#13+#10;
              end;
         if PolarisationType='with SF high field'
         then begin
              str(Ruu[i]:11:9,S1);S:=S+S1+#9;
              str(Rdd[i]:11:9,S1);S:=S+S1+#9;
              str(Rud[i]:11:9,S1);S:=S+S1+#9;
              str(Rdu[i]:11:9,S1);S:=S+S1+#13+#10;
              end;
         StrPCopy(SPChar,S);
         StrCat(ContenuClipBoard,SPchar);
     end;

     StringGlobalHandle:=GlobalAlloc(gmem_Moveable,65534);
     if StringGlobalHandle<>0 then
     begin
          StringGlobalPtr:=GlobalLock(StringGlobalHandle);
          if StringGlobalPtr<>nil then
          begin
               StrCopy(StringGlobalPtr,ContenuClipBoard);
               GlobalUnlock(StringGlobalHandle);
               with ClipBoard do
               begin try
                     open;
                     EmptyClipBoard;
                     SetClipBoardData(cf_Text, StringGlobalHandle);
                     CloseClipBoard;
                     finally close end;
               end
          end
          else GlobalFree(StringGlobalHandle);
     end;
     StrDispose(ContenuClipboard);
end;

procedure TDataReflectivity.Normalise(normfact:double);
begin
    R.scale(NormFact);T.scale(NormFact);
    Rup.scale(NormFact);Rdo.scale(NormFact);Tup.scale(NormFact);Tdo.scale(NormFact);
    Ruu.scale(NormFact);Rdd.scale(NormFact);Rud.scale(NormFact);Rdu.scale(NormFact);
    Tuu.scale(NormFact);Tdd.scale(NormFact);Tud.scale(NormFact);Tdu.scale(NormFact);
end;

{ drop all the points outside the range [abscMin, abscMax] }
{ INPUT : abscMin,AbscMax:double, the crop range           }
{ OUTPUT : the data points outside this range have been dropped }
procedure TDataReflectivity.Crop(abscMin,AbscMax:double);
var i,imin,nbrPointsExp:integer;
    aCrt:double;
    nbrPoints:integer;
    copyFinished:boolean;
begin
   nbrPoints:=Abscisses.length;
   { on redefinit les tailles aux cas ou certains vecteurs n'aient pas de taille définie }
   { tous les vecteurs ne sont pas forcéments initialisés               }
    R.size(nbrPoints,false);Rs.size(nbrPoints,false);
    Rup.size(nbrPoints,false);RupS.size(nbrPoints,false);
    Rdo.size(nbrPoints,false);RdoS.size(nbrPoints,false);
    Ruu.size(nbrPoints,false);RuuS.size(nbrPoints,false);
    Rdd.size(nbrPoints,false);RddS.size(nbrPoints,false);
    Rud.size(nbrPoints,false);RudS.size(nbrPoints,false);
    Rdu.size(nbrPoints,false);RduS.size(nbrPoints,false);

   { check if the range is not outside the actual available values }
   if abscMin<Abscisses[0] then abscMin:=Abscisses[0];
   if abscMax>Abscisses[Abscisses.length-1] then abscMax:=Abscisses[Abscisses.length-1];
   imin:=0;
   { recherche du premier indice du range }
   while Abscisses[imin]<abscMin do inc(imin);
   i:=0;
   { il vaudrait mieux utiliser la procedure copy ! }
   { pas sur a cause des overhaeds de creation des différents tableaux }
   copyFinished:=false;
   while not(copyFinished) do
   begin
        Abscisses[i]:=Abscisses[imin+i];
        Angles[i]:=angles[imin+i];
        Qz[i]:=Qz[imin+i];
        R[i]:=R[imin+i];Rs[i]:=Rs[imin+i];
        Rup[i]:=Rup[imin+i];RupS[i]:=RupS[imin+i];
        Rdo[i]:=Rdo[imin+i];RdoS[i]:=RdoS[imin+i];
        Ruu[i]:=Ruu[imin+i];RuuS[i]:=RuuS[imin+i];
        Rdd[i]:=Rdd[imin+i];RddS[i]:=RddS[imin+i];
        Rud[i]:=Rud[imin+i];RudS[i]:=RudS[imin+i];
        Rdu[i]:=Rdu[imin+i];RduS[i]:=RduS[imin+i];
        inc(i);
        if (imin+i)<nbrPoints
        then begin if Abscisses[imin+i]>abscMax then copyFinished:=true end
        else copyFinished:=true;
   end;
   nbrPointsExp:=i;

    Angles.size(nbrPointsExp,false);Qz.size(nbrPointsExp,false);Abscisses.size(nbrPointsExp,false);
    R.size(nbrPointsExp,false);Rs.size(nbrPointsExp,false);
    Rup.size(nbrPointsExp,false);RupS.size(nbrPointsExp,false);
    Rdo.size(nbrPointsExp,false);RdoS.size(nbrPointsExp,false);
    Ruu.size(nbrPointsExp,false);RuuS.size(nbrPointsExp,false);
    Rdd.size(nbrPointsExp,false);RddS.size(nbrPointsExp,false);
    Rud.size(nbrPointsExp,false);RudS.size(nbrPointsExp,false);
    Rdu.size(nbrPointsExp,false);RduS.size(nbrPointsExp,false);
end;

{ copies a reflectivity data set                                                }
procedure TDataReflectivity.Clone(MyD:TDataReflectivity);
begin
    name:=MyD.name;
    particles:=MyD.particles;
    TimeOfFlight:=MyD.TimeOfFLight;
    PolarisationType:=MyD.PolarisationType;
    weighting:=MyD.Weighting;
    convolution:=MyD.convolution;
    convolutionCalculation:=MyD.convolutionCalculation;
    AbscissesUnits:=MyD.AbscissesUnits;
    angles.copy(MyD.angles);
    Qz.copy(MyD.Qz);QzSig.copy(MyD.QzSig);
    dQzNbr:=MyD.dQzNbr;
    QzMult:=MyD.QzMult;
    abscisses.copy(MyD.Abscisses);
    lambdaSpectrum.copy(MyD.lambdaSpectrum);lambdaIntensity.copy(MyD.lambdaIntensity);
    thetaSpectrum.copy(MyD.thetaSpectrum);thetaIntensity.copy(MyD.thetaIntensity);
    QzSpectrum:=MyD.QzSpectrum;
    QzIntensity.copy(MyD.QzIntensity);
    QzFast.copy(MyD.QzFast);
    IdxInQzFastBot:=MyD.IdxInQzFastBot;IdxInQzFastTop:=MyD.IdxInQzFastTop;
    { vecteur des amplitudes réfléchies et transmises (complexes) }
    R.copy(MyD.R);Rup.copy(MyD.Rup);Rdo.copy(MyD.Rdo);T.copy(MyD.T);Tup.copy(MyD.Tup);Tdo.copy(MyD.Tdo);AR.copy(MyD.AR);AT.copy(MyD.AT);
    Rs.copy(MyD.Rs);RupS.copy(MyD.RupS);RdoS.copy(MyD.RdoS);
    Ruu.copy(MyD.Ruu);Rdd.copy(MyD.Rdd);Rdu.copy(MyD.Rdu);Rud.copy(MyD.Rud);Tuu.copy(MyD.Tuu);Tdd.copy(MyD.Tdd);Tdu.copy(MyD.Tdu);Tud.copy(MyD.Tud);
    RuuS.copy(MyD.RuuS);RddS.copy(MyD.RddS);RudS.copy(MyD.RudS);RduS.copy(MyD.RduS);
end;

{ ============================================================================= }
{ méthodes associées à la classe TExperimentalParameters                        }
{ ============================================================================= }
constructor TExperimentalParameters.Create;
var i:integer;
begin
   inherited Create;
   createIt(lambdaSpectrum,lambdaIntensity);
   createIt(thetaSpectrum,thetaIntensity);
end;

{ mise a jour de la grille de description des paramètres à partir d'une variable  }
{ il faut determiner la taille de la grille dans laquelle on veut ecrire les data }
procedure TExperimentalParameters.UpdateGridFromVarExpParam(var MyGrid,MyLambdaGrid,MyThetaGrid:TAdvStringGrid);
var s,sU:string;
    i:integer;
    versionSR:double;
begin
  versionSR:=versionNr;
  { on scan les lignes les unes apres les autres et on met les paramètres correspondnats a jour }
  with MyGrid do
    for i:=1 to rowCount do
    begin
     sU:=Uppercase(cells[0,i]);s:=cells[1,i];
     if sU='VERSION' then floats[1,i]:=versionSR;
     if sU='COMMENT' then cells[1,i]:=calculatedDataComment;
     if sU='ABSCISSE UNITS' then cells[1,i]:=AbscissesUnits;
     { experimental parameters }
     if sU='EXPERIMENTAL PARAMETERS' then colors[0,i]:=clRed;
     if sU='POLARISATION' then cells[1,i]:=PolarisationType;
     if sU='TOF' then
          if Tof=true then cells[1,i]:='TRUE' else cells[1,i]:='FALSE';
     if sU='CENTRAL LAMBDA' then floats[1,i]:=lambda*1e9;
     if sU='DELTA LAMBDA' then floats[1,i]:=dlambda*1e9;
     if sU='NR. LAMBDA POINTS' then ints[1,i]:=dlambdaNBr;
     if sU='LAMBDA WIDTH MULTIPLIER' then floats[1,i]:=lambdaMult;
     if sU='INCIDENCE ANGLE' then floats[1,i]:=theta*180/pi;
     if sU='DELTA THETA' then floats[1,i]:=dtheta*180/pi;
     if sU='NR. THETA POINTS' then ints[1,i]:=dthetaNBr;
     if sU='THETA WIDTH MULTIPLIER' then floats[1,i]:=ThetaMult;
     if su='CONVOLUTION' then cells[1,i]:=convolution;
     if su='CONVOLUTION CALCULATION' then cells[1,i]:=convolutionCalculation;
     if sU='NR. POINTS IN DQZ' then ints[1,i]:=dQzNbr;
     if sU='QZ WIDTH MULTIPLIER' then floats[1,i]:=QzMult;     
     if sU='FWHM FRACTION' then floats[1,i]:=FWHMFraction;
     if sU='PARTICLES' then cells[1,i]:=particles;
     if sU='SCAN RANGE MIN' then floats[1,i]:=ScanRangeMin;
     if sU='SCAN RANGE STEP' then floats[1,i]:=ScanRangeStep;
     if sU='SCAN RANGE MAX' then floats[1,i]:=ScanRangeMax;
     if sU='BEAM WIDTH' then floats[1,i]:=VertSize;
     { FIELD }
     if sU='FIELD' then colors[0,i]:=clRed;
     if sU='FIELD MAGNITUDE' then floats[1,i]:=FieldMag*1000; { on passe de T à mT }
     if sU='FIELD THETA' then floats[1,i]:=FieldTheta*180/pi;        { on passe de radians à ° }
     if sU='FIELD PHI' then floats[1,i]:=FieldPhi*180/pi;
     { MULTILAYERS }
     if sU='MULTILAYERS' then colors[0,i]:=clRed;
     if sU='NR. OF MULTILAYERS' then ints[1,i]:=nbrML;
     if sU='THICKNESS FUNCTION' then cells[1,i]:=thicknessFunction;
     if sU='USE THICKNESS FUNCTION' then
        if UseThicknessFunction then cells[1,i]:='TRUE' else cells[1,i]:='FALSE';
     if sU='ROUGHNESS FUNCTION' then cells[1,i]:=roughnessFunction;
     if sU='USE ROUGHNESS FUNCTION' then
        if UseRoughnessFunction then cells[1,i]:='TRUE' else cells[1,i]:='FALSE';
     if sU='MAGNETIZATION FUNCTION' then cells[1,i]:=magnetizationFunction;
     if sU='USE MAGNETIZATION FUNCTION' then
        if UseMagnetizationFunction then cells[1,i]:='TRUE' else cells[1,i]:='FALSE';
     if sU='PHI FUNCTION' then cells[1,i]:=phiFunction;
     if sU='USE PHI FUNCTION' then
        if UsePhiFunction then cells[1,i]:='TRUE' else cells[1,i]:='FALSE';
     { FITTING PARAMETERS }
     if sU='FITTING PARAMETERS' then colors[0,i]:=clRed;
     if sU='NORMALISATION' then floats[1,i]:=NormalisationFactor;
     if sU='NORMALISATION STEP' then floats[1,i]:=NormalisationFactorStep;
     if sU='FITTING NORMALISATION' then
        if FitNormalisation=true then cells[1,i]:='TRUE' else cells[1,i]:='FALSE';
     if sU='NR. ITERATIONS' then ints[1,i]:=NbrIterations;
     if sU='RANDOMNESS' then ints[1,i]:=randomness;
     if sU='WEIGHTING' then cells[1,i]:=PonderationType;
     if sU='FITSTEP' then floats[1,i]:=FitStep;
     if sU='ABSCISSE MIN' then floats[1,i]:=FitAbscisseMin;
     if sU='ABSCISSE MIN' then floats[1,i]:=FitAbscisseMax;
     { DIFFUSE }
     if sU='DIFFUSE' then colors[0,i]:=clRed;
     if sU='DIFFUSE SCAN TYPE' then cells[1,i]:=scanType;
     if sU='THETADIFF MIN' then floats[1,i]:=ThetaDiffMin;
     if sU='THETADIFF MAX' then floats[1,i]:=ThetaDiffMax;
     if sU='THETADIFF STEP' then floats[1,i]:=ThetaDiffStep;
     if sU='2THETADIFF MIN' then floats[1,i]:=DeuxThetaDiffMin;
     if sU='2THETADIFF MAX' then floats[1,i]:=DeuxThetaDiffMax;
     if sU='2THETADIFF STEP' then floats[1,i]:=DeuxThetaDiffStep;
     if sU='AUTORANGEDIFF' then
        if AutoRangeDiff=true then cells[1,i]:='TRUE' else cells[1,i]:='FALSE';
     if sU='ABSCISSEDIFF' then cells[1,i]:=AbscisseDiffType;
     if sU='FORMALISMDIFF' then cells[1,i]:=FormalismDiffType;
     { PROFILES }
{     if sU='PROFILED' then
        if Profiled=true then cells[1,i]:='TRUE' else cells[1,i]:='FALSE'; }
  end;
  { mise a jour du spectre en lambda }
  { on copie le spectre en lambda dans la feuille, meme si celui ci est simplement gaussien }
{  MyLambdaGrid.Clear;
  MyThetaGrid.clear; }
  { mise a jour des en-tetes }
{  MyLambdaGrid.cells[0,0]:='/';MyLambdaGrid.cells[1,0]:='lambda (nm)';MyLambdaGrid.cells[2,0]:='weight';
  MyThetaGrid.cells[0,0]:='/';MyThetaGrid.cells[1,0]:='d theta (°)';MyThetaGrid.cells[2,0]:='weight';
  for i:=0 to lambdaSpectrum.length-1 do
  begin
        MyLambdaGrid.ints[0,i+1]:=i+1;
        MyLambdaGrid.floats[1,i+1]:=lambdaSpectrum[i]*1e9;MyLambdaGrid.floats[2,i+1]:=lambdaIntensity[i];
  end;
  for i:=0 to thetaSpectrum.length-1 do
  begin
        MyThetaGrid.ints[0,i+1]:=i+1;
        MyThetaGrid.floats[1,i+1]:=thetaSpectrum[i]*180/pi;MyThetaGrid.floats[2,i+1]:=thetaIntensity[i];
  end; }
end;

{ mise a jour d'une variable à partir d'une grille de decsription des paramètres  }
{ il faut determiner la taille de la grille dans laquelle on veut ecrire les data }
procedure TExperimentalParameters.UpdateVarExpParamFromGrid(var MyGrid,MyLambdaGrid,MyThetaGrid:TAdvStringGrid);
var s,sU:string;
    i,nbrPoints:integer;
begin
  { on scan les lignes les unes apres les autres et on met les paramètres correspondants a jour }
  with MyGrid do
    for i:=1 to rowCount do
    begin
     sU:=Uppercase(cells[0,i]);
{     chaine:=cells[1,i];valeur:=floats[1,i];valeurInt:=ints[1,i]; }
     if sU='COMMENT' then calculatedDataComment:=cells[1,i];
     if sU='ABSCISSE UNITS' then AbscissesUnits:=cells[1,i];
     { experimental parameters }
     if sU='EXPERIMENTAL PARAMETERS' then colors[0,i]:=clRed;
     if sU='POLARISATION' then PolarisationType:=cells[1,i];
     if sU='TOF' then
          if cells[1,i]='TRUE' then Tof:=true else Tof:=false;
     if sU='CENTRAL LAMBDA' then lambda:=floats[1,i]*1e-9;
     if sU='DELTA LAMBDA' then dlambda:=floats[1,i]*1e-9;
     if sU='NR. LAMBDA POINTS' then dlambdaNBr:=ints[1,i];
     if sU='LAMBDA WIDTH MULTIPLIER' then lambdaMult:=floats[1,i];
     if sU='INCIDENCE ANGLE' then theta:=floats[1,i]*pi/180;
     if sU='DELTA THETA' then dtheta:=floats[1,i]*pi/180;
     if sU='NR. THETA POINTS' then dthetaNBr:=ints[1,i];
     if sU='THETA WIDTH MULTIPLIER' then ThetaMult:=floats[1,i];
     if su='CONVOLUTION' then convolution:=cells[1,i];
     if su='CONVOLUTION CALCULATION' then convolutionCalculation:=cells[1,i];
     if sU='NR. POINTS IN DQZ' then dQzNbr:=ints[1,i];
     if sU='QZ WIDTH MULTIPLIER' then QzMult:=floats[1,i];
     if sU='FWHM FRACTION' then FWHMFraction:=floats[1,i];
     if sU='PARTICLES' then particles:=cells[1,i];
     if sU='SCAN RANGE MIN' then ScanRangeMin:=floats[1,i];
     if sU='SCAN RANGE STEP' then ScanRangeStep:=floats[1,i];
     if sU='SCAN RANGE MAX' then ScanRangeMax:=floats[1,i];
     if sU='BEAM WIDTH' then VertSize:=floats[1,i];
     { FIELD }
     if sU='FIELD' then colors[0,i]:=clRed;
     if sU='FIELD MAGNITUDE' then FieldMag:=floats[1,i]*0.001;         { on passe de mT à T }
     if sU='FIELD THETA' then FieldTheta:=floats[1,i]*pi/180;          { on passe de degré à radians }
     if sU='FIELD PHI' then FieldPhi:=floats[1,i]*pi/180;
     { MULTILAYERS }
     if sU='MULTILAYERS' then colors[0,i]:=clRed;
     if sU='NR. OF MULTILAYERS' then nbrML:=ints[1,i];
     if sU='THICKNESS FUNCTION' then thicknessFunction:=cells[1,i];
     if sU='USE THICKNESS FUNCTION' then
        if cells[1,i]='TRUE' then UseThicknessFunction:=true else UseThicknessFunction:=false;
     if sU='ROUGHNESS FUNCTION' then roughnessFunction:=cells[1,i];
     if sU='USE ROUGHNESS FUNCTION' then
        if cells[1,i]='TRUE' then UseRoughnessFunction:=true else UseRoughnessFunction:=false;
     if sU='MAGNETIZATION FUNCTION' then magnetizationFunction:=cells[1,i];
     if sU='USE MAGNETIZATION FUNCTION' then
        if cells[1,i]='TRUE' then UseMagnetizationFunction:=true else UseMagnetizationFunction:=false;
     if sU='PHI FUNCTION' then phiFunction:=cells[1,i];
     if sU='USE PHI FUNCTION' then
        if cells[1,i]='TRUE' then UsePhiFunction:=true else UsePhiFunction:=false;
     { FITTING PARAMETERS }
     if sU='FITTING PARAMETERS' then colors[0,i]:=clRed;
     if sU='NORMALISATION' then NormalisationFactor:=floats[1,i];
     if sU='NORMALISATIONSTEP' then NormalisationFactorStep:=floats[1,i];
     if sU='FITTING NORMALISATION' then
        if cells[1,i]='TRUE' then FitNormalisation:=true else FitNormalisation:=false;
     if sU='NR. ITERATIONS' then NbrIterations:=ints[1,i];
     if sU='RANDOMNESS' then randomness:=ints[1,i];
     if sU='FITSTEP' then FitStep:=floats[1,i];
     if sU='ABSCISSE MIN' then FitAbscisseMin:=floats[1,i];
     if sU='ABSCISSE MAX' then FitAbscisseMax:=floats[1,i];
     if sU='WEIGHTING' then begin {PonderationType:=cells[1,i]; }
                            { on change la nomenclature du weighting }
                            { par défaut, on impose 'linear' }
                            if (cells[1,i]<>'log') or (cells[1,i]<>'log²')
                            then PonderationType:='linear' else PonderationType:=cells[1,i];
                            end;
     { DIFFUSE }
     if sU='DIFFUSE' then colors[0,i]:=clRed;
     if sU='DIFFUSE SCAN TYPE' then scanType:=cells[1,i];
     if sU='THETADIFF MIN' then ThetaDiffMin:=floats[1,i];
     if sU='THETADIFF MAX' then ThetaDiffMax:=floats[1,i];
     if sU='THETADIFF STEP' then ThetaDiffStep:=floats[1,i];
     if sU='2THETADIFF MIN' then DeuxThetaDiffMin:=floats[1,i];
     if sU='2THETADIFF MAX' then DeuxThetaDiffMax:=floats[1,i];
     if sU='2THETADIFF STEP' then DeuxThetaDiffStep:=floats[1,i];
     if sU='AUTORANGEDIFF' then
        if cells[1,i]='TRUE' then AutoRangeDiff:=true else AutoRangeDiff:=false;
     if sU='ABSCISSEDIFF' then AbscisseDiffType:=cells[1,i];
     if sU='FORMALISMDIFF' then FormalismDiffType:=cells[1,i];
     { PROFILES }
{     if sU='PROFILED' then
        if cells[1,i]='TRUE' then Profiled:=true else profiled:=false; }
  end;
  { mise a jour des spectres ent theta et en lambda }
  UpdateDistributions(MyLambdaGrid,MyThetaGrid);
end;

{ update d'un jeu de paramètres experimentaux }
{ ces distributions sont valables en temps de vol et a longueur d'onde fixe }
procedure TExperimentalParameters.updateDistributions(var MyLambdaGrid,MyThetaGrid:TAdvStringGrid);
var i,j:integer;
    dlNbr,dtNbr,nbrPoints:integer;
    l,dl,dt,theta0,dQz:double; { angle d'incidence }
    dQzStep,TotalIntensity,sig:double;
begin
{ il faut traiter quatre cas différents en fonction du type de convolution }
{ si on n'est pas en full, il faut générer un spectre gaussien calculé     }
if convolution = 'full theta & lambda'
then begin { on lit les spectres a partir des feuilles }
     lambdaSpectrum.size(nbrConvMax,false);lambdaIntensity.size(nbrConvMax,false);
     thetaSpectrum.size(nbrConvMax,false);thetaIntensity.size(nbrConvMax,false);
     dlNbr:=0;
     for i:=0 to nbrConvMax-1 do
     begin
        if MyLambdaGrid.floats[0,i+1]<>0
        then begin lambdaSpectrum[i]:=MyLambdaGrid.floats[1,i+1]*1e-9;
                   lambdaIntensity[i]:=MyLambdaGrid.floats[2,i+1];inc(dlNbr) end;
     end;
     lambdaSpectrum.size(dlNbr,false);lambdaIntensity.size(dlNbr,false);
     dtNbr:=0;
     for i:=0 to nbrConvMax-1 do
     begin
        if MyThetaGrid.floats[0,i+1]<>0
        then begin thetaSpectrum[i]:=MyThetaGrid.floats[1,i+1]*pi/180;
                   thetaIntensity[i]:=MyThetaGrid.floats[2,i+1];inc(dtNbr) end;
     end;
     thetaSpectrum.size(dtNbr,false);thetaIntensity.size(dtNbr,false);
     end;
if convolution = 'full theta'
then begin { on génère un spectre gaussien pour lambda };
     thetaSpectrum.size(nbrConvMax,false);thetaIntensity.size(nbrConvMax,false);
     dtNbr:=0;
     for i:=0 to nbrConvMax-1 do
     begin
        if MyThetaGrid.floats[0,i+1]<>0
        then begin thetaSpectrum[i]:=MyThetaGrid.floats[1,i+1]*pi/180;
                   thetaIntensity[i]:=MyThetaGrid.floats[2,i+1];inc(dtNbr) end;
     end;
     thetaSpectrum.size(dtNbr,false);thetaIntensity.size(dtNbr,false);

     TotalIntensity:=0;
     lambdaSpectrum.size(dlambdaNbr,false);lambdaIntensity.size(dlambdaNbr,false);
     dlNbr:=trunc(dlambdaNbr/2);
     l:=lambda;sig:=dlambda/2;
     if dlambdaNbr>1 then dl:=lambdaMult*dlambda*sqrt(2*ln(2))/(dlambdaNbr-1) else dl:=0;
     for i:=-dlNbr to dlNbr do
     begin
          lambdaSpectrum[i+dlNbr]:=l+i*dl;
          lambdaIntensity[i+dlNbr]:=1/(sig*sqrt(2*pi))*exp(-(sqr(i*dl)/(2*sig*sig)));
          TotalIntensity:=TotalIntensity+lambdaIntensity[i+dlNbr];
     end;
     lambdaIntensity.scale(1/TotalIntensity);
    end;
if convolution = 'full lambda'
then begin { on génère un spectre gaussien pour lambda };
     lambdaSpectrum.size(nbrConvMax,false);lambdaIntensity.size(nbrConvMax,false);
     dlNbr:=0;
     for i:=0 to nbrConvMax-1 do
     begin
        if MyLambdaGrid.floats[0,i+1]<>0
        then begin lambdaSpectrum[i]:=MyLambdaGrid.floats[1,i+1]*1e-9;
                   lambdaIntensity[i]:=MyLambdaGrid.floats[2,i+1];inc(dlNbr) end;
     end;
     lambdaSpectrum.size(dlNbr,false);lambdaIntensity.size(dlNbr,false);

     TotalIntensity:=0;
     thetaSpectrum.size(dThetaNbr,false);thetaIntensity.size(dThetaNbr,false);
     dtNbr:=trunc(dThetaNbr/2); { on suppose que ce nombre est impair }
     if dThetaNbr>1 then dt:=thetaMult*dtheta*sqrt(2*ln(2))/(dThetaNbr-1) else dt:=0;
     sig:=dTheta/2;
     for i:=-dtNbr to dtNbr do
     begin
          thetaSpectrum[i+dtNbr]:=i*dt+0.000001;
          thetaIntensity[i+dtNbr]:=1/(sig*sqrt(2*pi))*exp(-(sqr(i*dt)/(2*sig*sig)));
          TotalIntensity:=TotalIntensity+thetaIntensity[i+dtNbr];
     end;
    thetaIntensity.scale(1/TotalIntensity);
    end;
if convolution = 'gaussian'
then begin { on génère un spectre gaussien en Qz directement };
     { on génère aussi des spectres de résolution en theta et en lambda }
     { spectre Gaussien en Theta }
     TotalIntensity:=0;
     thetaSpectrum.size(dThetaNbr,false);thetaIntensity.size(dThetaNbr,false);
     dtNbr:=trunc(dThetaNbr/2); { on suppose que ce nombre est impair }
     sig:=dtheta/2;
     if dThetaNbr>1 then dt:=thetaMult*dtheta*sqrt(2*ln(2))/(dThetaNbr-1) else dt:=0;
     for i:=-dtNbr to dtNbr do
     begin
          thetaSpectrum[i+dtNbr]:=i*dt+0.000001;
          thetaIntensity[i+dtNbr]:=1/(sig*sqrt(2*pi))*exp(-(sqr(i*dt)/(2*sig*sig)));
          TotalIntensity:=TotalIntensity+thetaIntensity[i+dtNbr];
     end;
     thetaIntensity.scale(1/TotalIntensity);
     { spectre Gaussien en lambda }
     TotalIntensity:=0;
     lambdaSpectrum.size(dlambdaNbr,false);lambdaIntensity.size(dlambdaNbr,false);
     dlNbr:=trunc(dlambdaNbr/2);
     l:=lambda;sig:=dlambda/2;
     if dlambdaNbr>1 then dl:=lambdaMult*dlambda*sqrt(2*ln(2))/(dlambdaNbr-1) else dl:=0;
     for i:=-dlNbr to dlNbr do
     begin
          lambdaSpectrum[i+dlNbr]:=l+i*dl;
          lambdaIntensity[i+dlNbr]:=1/(sig*sqrt(2*pi))*exp(-(sqr(i*dl)/(2*sig*sig)));
          TotalIntensity:=TotalIntensity+lambdaIntensity[i+dlNbr];
     end;
     lambdaIntensity.scale(1/TotalIntensity);
   end;

end;

{ ============================================================================= }
{ méthodes associées à la classe TMultiLayer                                    }
{ ============================================================================= }

constructor TMultiLayer.Create;
begin
     inherited Create;
     createIt(z,d);
     createIt(bn,bni,f,fi);
     createIt(Aim,theta,phi);
     createIt(sigma,xsi,exph);
     createIt(V,Vi,Vup,Vdo);
     createIt(BeffMag, BeffTheta, BeffPhi);
     createIt(zML,dML);
     createIt(bnML,bniML,fML,fiML);
     createIt(AimML,thetaML,phiML);
     createIt(sigmaML,xsiML,exphML);
     createIt(VML,ViML,VupML,VdoML);
     createIt(BeffMagML,BeffThetaML,BeffPhiML);
end;

procedure TMultiLayer.Clone(MyML:TmultiLayer);
var i:integer;
begin
    nbrLayers:=MyML.nbrLayers;nbrParam:=MyML.nbrParam;
    description:=MyML.Description;
{ YN }   zYN:=MyML.zYN;dYN:=MyML.dYN;bYN:=MyML.bYN;biYN:=MyML.biYN;fYN:=MyML.fYN;fiYN:=MyML.fiYN;
    MYN:=MyML.MYN;thetaYN:=MyML.thetaYN;PhiYN:=MyML.PhiYN;sigYN:=MyML.sigYN;MLYN:=MyML.MLYN;
{ corr } zCorr:=MyML.zCorr;dCorr:=MyML.dCorr;bCorr:=MyML.bCorr;biCorr:=MyML.biCorr;fCorr:=MyML.fCorr;fiCorr:=MyML.fiCorr;
    MCorr:=MyML.MCorr;thetaCorr:=MyML.thetaCorr;PhiCorr:=MyML.PhiCorr;sigmaCorr:=MyML.sigmaCorr;
{ step } zStep:=MyML.zStep;dStep:=MyML.dStep;bStep:=MyML.bStep;biStep:=MyML.biStep;
    fStep:=MyML.fStep;fiStep:=MyML.fiStep;AimStep:=MyML.AimStep;thetaStep:=MyML.thetaStep;PhiStep:=MyML.PhiStep;
    sigmaStep:=MyML.sigmaSTep;MLStep:=MyML.MLStep;
    
    MLSystem:=MyML.MLSystem;
    layerIdx:=MyML.layerIdx;
    profile:=MyML.profile;
    profiled:=MyML.profiled;
    z.Copy(MyML.z);d.Copy(MyML.d);
    bn.Copy(MyML.bn);bni.Copy(MyML.bni);
    f.Copy(MyML.f);fi.Copy(MyML.fi);
    Aim.Copy(MyML.Aim);theta.Copy(MyML.theta);phi.Copy(MyML.phi);
    sigma.Copy(MyML.sigma);xsi.Copy(MyML.xsi);exph.Copy(MyML.exph);
    BeffMag.Copy(MyML.BeffMag);BeffTheta.Copy(MyML.BeffTheta);BeffPhi.Copy(MyML.BeffPhi);
    V.Copy(MyML.V);Vi.Copy(MyML.Vi);Vup.Copy(MyML.Vup);Vdo.Copy(MyML.Vdo);
    zML.Copy(MyML.zML);dML.Copy(MyML.dML);bnML.Copy(MyML.bnML);bniML.Copy(MyML.bniML);fML.Copy(MyML.fML);fiML.Copy(MyML.fiML);
    AimML.Copy(MyML.AimML);thetaML.Copy(MyML.thetaML);phiML.Copy(MyML.PhiML);
    sigmaML.Copy(MyML.sigmaML);xsiML.Copy(MyML.xsiML);exphML.Copy(MyML.exphML);
    VML.Copy(MyML.VML);ViML.Copy(MyML.ViML);VupML.Copy(MyML.VupML);VdoML.Copy(MyML.VdoML);
    BeffMagML.Copy(MyML.BeffMagML);BeffThetaML.Copy(MyML.BeffThetaML);BeffPhiML.Copy(MyML.BeffPhiML);
end;

{ mise à jour des potentiels V  dans la multicouche }
procedure TMultiLayer.UpdatePotential(MyEP:TExperimentalParameters);
var i:integer;
begin
   if MyEP.particles='neutrons'
   then for i:=0 to nbrLayers-2 do
          begin V[i]:=Pihm*d[i]*bn[i];Vi[i]:=-Pihm*d[i]*bni[i];
                Vup[i]:=V[i]-Aim[i]*mun;Vdo[i]:=V[i]+Aim[i]*mun; end
   else for i:=0 to nbrLayers-2 do
          begin V[i]:=Cte*d[i]*f[i];Vi[i]:=-Cte*d[i]*fi[i]; end;
end;

{ MAJ des paramètres a partir d'une grille                       }
{ INPUT : MyGrid, une grille contenant la description du système }
procedure TMultiLayer.UpdateFromMultiLayerGrid(var MyGrid:TAdvStringGrid;var MyExperimentalParameters:TExperimentalParameters);
var i:integer;
    lambda,step:double;
    BeffX,BeffY,BeffZ:double;
    FitCol,delta,beta:double;
    zz,bb,bibi,ff,fifi,dd,MM,tt,phiphi,sig,xsixsi,exphexph:double;
    FitColS:string;
    carac:char;
begin
    lambda:=MyExperimentalParameters.lambda;
    step:=MyExperimentalParameters.FitStep/100;
    { determination du nombre de lignes dans le tableau }
    z.size(nbrLayersMax,false);
    bn.size(nbrLayersMax,false);bni.size(nbrLayersMax,false);
    f.size(nbrLayersMax,false);fi.size(nbrLayersMax,false);
    d.size(nbrLayersMax,false);
    Aim.size(nbrLayersMax,false);theta.size(nbrLayersMax,false);phi.size(nbrLayersMax,false);
    BeffMag.size(nbrLayersMax,false);BeffTheta.size(nbrLayersMax,false);BeffPhi.size(nbrLayersMax,false);
    V.size(nbrLayersMax,false);Vi.size(nbrLayersMax,false);
    Vup.size(nbrLayersMax,false);Vdo.size(nbrLayersMax,false);
    sigma.size(nbrLayersMax,false);xsi.size(nbrLayersMax,false);exph.size(nbrLayersMax,false);
    { update de tous les vecteurs decrivant la ML }
    nbrLayers:=0;nbrParam:=0;
    i:=1;
    MyGrid.update;
    while (i<=NbrLayersMax) do
    begin
{    DataReflecForm.TestGrid.Rows[i]:=MyGrid.Rows[i]; }
    if MyGrid.Cells[ColYN,i]='Y'
         then
         begin
            with MyGrid do
            begin
              LayerIdx[nbrLayers]:=i;  { indice de la ligne du tableau qui definit la couche }
              colors[ColEpaisseur,i]:=clWhite;colors[ColDensity,i]:=clWhite;
              colors[ColB,i]:=clWhite;colors[ColBi,i]:=clWhite;
              colors[ColM,i]:=clWhite;colors[ColTheta,i]:=clWhite;colors[ColPhi,i]:=clWhite;
              colors[ColSigma,i]:=clWhite;colors[ColXsi,i]:=clWhite;colors[Colh,i]:=clWhite;
              colors[ColMLYN,i]:=clWhite;colors[ColProfile,i]:=clWhite;              
              colors[ColYN,i]:=clSkyBlue;colors[ColDescription,i]:=clSkyBlue;
              Colors[ColEpaisseurYN,i]:=clSilver;Colors[ColDensityYN,i]:=clSilver;
              Colors[ColBYN,i]:=clSilver;Colors[ColBiYN,i]:=clSilver;
              Colors[ColMYN,i]:=clSilver;Colors[ColThetaYN,i]:=clSilver;Colors[ColPhiYN,i]:=clSilver;
              Colors[ColSigYN,i]:=clSilver;Colors[ColXsiYN,i]:=clSilver;Colors[ColhYN,i]:=clSilver;
{              LayerIdx[nbrLayers]:=i; }
              Description[nbrLayers]:=cells[ColDescription,i];
              { information sur le profil }
{              ProfNr[nbrLayers]:=numberRC[i,ColProfNr];
              ProfPond[nbrLayers]:=numberRC[i,ColProfPond];
              ProfType[nbrLayers]:=GetStrF1(i,ColProfType); }
              { epaisseur }
              zz:=floats[ColEpaisseur,i]*1e-9;z[nbrLayers]:=zz;
              FitColS:=Uppercase(cells[colEpaisseurYN,i]);FitCol:=abs(floats[colEpaisseurYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin zYN[nbrLayers]:=0;zStep[nbrLayers]:=step*zz;inc(nbrParam);zCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin zYN[nbrLayers]:=0;zStep[nbrLayers]:=step*zz;inc(nbrParam);zCorr[nbrLayers]:=carac;  end
                   else if FitCol<>0
                        then begin zYN[nbrLayers]:=0;zStep[nbrLayers]:=FitCol*1e-9;inc(nbrParam);zCorr[nbrLayers]:='0' end
                        else begin zYN[nbrLayers]:=-1;zStep[nbrLayers]:=0;zCorr[nbrLayers]:='0' end;

              { densité }
              dd:=floats[ColDensity,i]*1e28;d[nbrLayers]:=dd;
              FitColS:=Uppercase(cells[colDensityYN,i]);FitCol:=abs(Floats[colDensityYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin dYN[nbrLayers]:=0;dStep[nbrLayers]:=step*dd;inc(nbrParam);dCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin dYN[nbrLayers]:=0;dStep[nbrLayers]:=step*dd;inc(nbrParam);dCorr[nbrLayers]:=carac end
                   else if FitCol<>0 then begin dYN[nbrLayers]:=0;dStep[nbrLayers]:=FitCol*1e28;inc(nbrParam);dCorr[nbrLayers]:='0' end
                                     else begin dYN[nbrLayers]:=-1;dStep[nbrLayers]:=0;dCorr[nbrLayers]:='0' end;
{ cas des neutrons }
            if MyExperimentalParameters.Particles='neutrons' then
            begin
              { longueur de diffusion }
              bb:=Floats[ColB,i]*1e-15;bn[nbrLayers]:=bb;
              FitColS:=Uppercase(Cells[colBYN,i]);FitCol:=abs(Floats[colBYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin bYN[nbrLayers]:=0;bStep[nbrLayers]:=step*bb;inc(nbrParam);bCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin bYN[nbrLayers]:=0;bStep[nbrLayers]:=step*bb;inc(nbrParam);bCorr[nbrLayers]:=carac end
                   else if FitCol<>0 then begin bYN[nbrLayers]:=0;bStep[nbrLayers]:=FitCol*1e-15;inc(nbrParam);bCorr[nbrLayers]:='0' end
                                     else begin bYN[nbrLayers]:=-1;bStep[nbrLayers]:=0;bCorr[nbrLayers]:='0' end;
              { longueur de diffusion; composante complexe }
              bibi:=Floats[ColBi,i]*1e-15;bni[nbrLayers]:=bibi;
              FitColS:=Uppercase(Cells[colBiYN,i]);FitCol:=abs(Floats[colBiYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin biYN[nbrLayers]:=0;biStep[nbrLayers]:=step*bibi;inc(nbrParam);biCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin biYN[nbrLayers]:=0;biStep[nbrLayers]:=step*bibi;inc(nbrParam);biCorr[nbrLayers]:=carac end
                   else if FitCol<>0 then begin biYN[nbrLayers]:=0;biStep[nbrLayers]:=FitCol*1e-15;inc(nbrParam);biCorr[nbrLayers]:='0' end
                                     else begin biYN[nbrLayers]:=-1;biStep[nbrLayers]:=0;biCorr[nbrLayers]:='0' end;
              { lecture de l'aimantation sous la forme M,theta, phi }
              MM:=Floats[ColM,i]*mu0*muB*d[nbrLayers];Aim[nbrLayers]:=MM; { Aim est en T }
              FitColS:=Uppercase(Cells[colMYN,i]);FitCol:=abs(Floats[colMYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin MYN[nbrLayers]:=0;AimStep[nbrLayers]:=step*MM;inc(nbrParam);MCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin MYN[nbrLayers]:=0;AimStep[nbrLayers]:=step*MM;inc(nbrParam);MCorr[nbrLayers]:=carac end
                   else if FitCol<>0 then begin MYN[nbrLayers]:=0;AimStep[nbrLayers]:=FitCol*mu0*muB*d[nbrLayers];inc(nbrParam);MCorr[nbrLayers]:='0' end
                                     else begin MYN[nbrLayers]:=-1;AimStep[nbrLayers]:=0;MCorr[nbrLayers]:='0' end;
              { theta }
              tt:=Floats[ColTheta,i]*Pi/180;Theta[nbrLayers]:=tt;
              FitColS:=Uppercase(Cells[colThetaYN,i]);FitCol:=abs(Floats[colThetaYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin ThetaYN[nbrLayers]:=0;ThetaStep[nbrLayers]:=step*tt;inc(nbrParam);thetaCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin ThetaYN[nbrLayers]:=0;ThetaStep[nbrLayers]:=step*tt;inc(nbrParam);thetaCorr[nbrLayers]:=carac end
                   else if FitCol<>0 then begin ThetaYN[nbrLayers]:=0;ThetaStep[nbrLayers]:=FitCol*Pi/180;inc(nbrParam);thetaCorr[nbrLayers]:='0' end
                                     else begin ThetaYN[nbrLayers]:=-1;ThetaStep[nbrLayers]:=0;thetaCorr[nbrLayers]:='0' end;
              { phi }
              phiphi:=Floats[ColPhi,i]*Pi/180;Phi[nbrLayers]:=phiphi;
              FitColS:=Uppercase(Cells[colPhiYN,i]);FitCol:=abs(Floats[colPhiYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin PhiYN[nbrLayers]:=0;PhiStep[nbrLayers]:=step*phiphi;inc(nbrParam);phiCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin PhiYN[nbrLayers]:=0;PhiStep[nbrLayers]:=step*phiphi;inc(nbrParam);phiCorr[nbrLayers]:=carac end
                   else if FitCol<>0 then begin PhiYN[nbrLayers]:=0;PhiStep[nbrLayers]:=FitCol*Pi/180;inc(nbrParam);phiCorr[nbrLayers]:='0' end
                                     else begin PhiYN[nbrLayers]:=-1;PhiStep[nbrLayers]:=0;phiCorr[nbrLayers]:='0' end;
              { calcul du champ effectif dans chaque couche }
              BeffX:=MyExperimentalParameters.FieldMag*Sin(MyExperimentalParameters.fieldTheta)*Cos(MyExperimentalParameters.fieldPhi)
                        +Aim[nbrlayers]*Sin(theta[nbrlayers])*Cos(phi[nbrlayers]);
              BeffY:=MyExperimentalParameters.FieldMag*Sin(MyExperimentalParameters.fieldTheta)*Sin(MyExperimentalParameters.fieldPhi)
                        +Aim[nbrlayers]*Sin(theta[nbrlayers])*Sin(phi[nbrlayers]);
              BeffZ:=MyExperimentalParameters.FieldMag*Cos(MyExperimentalParameters.fieldTheta);
              BeffMag[nbrlayers]:= sqrt(sqr(BeffX)+sqr(BeffY)+sqr(BeffZ));
              {definition des angles théta et phi du champ efficace}
              if BeffMag[nbrlayers]<>0
              then BeffTheta[nbrlayers]:=ArcCos(BeffZ/BeffMag[nbrlayers]) else BeffTheta[nbrlayers]:=0; {en radians}
              if (BeffX<>0) and (BeffY<>0)
              then BeffPhi[nbrlayers]:=arg(Cplx(BeffX,BeffY)) else BeffPhi[nbrlayers]:=0;{en radians}

              { definition du potentiel d'interaction }
              V[nbrLayers]:=2*Pi*hb2*dd*bb/mn;
              Vi[nbrLayers]:=-2*Pi*hb2*dd*bibi/mn;
{ on ne peut tenir compte du champ fort que dans le cas 'with SF high field' }
              if MyExperimentalParameters.PolarisationType ='with SF high field'
              then begin
                   Vup[nbrLayers]:=V[nbrLayers]-mun*BeffMag[nbrLayers];   { cela signifie que meme dans le calcul with spin-flip on superpose le potentiel magnetique }
                   Vdo[nbrLayers]:=V[nbrLayers]+mun*BeffMag[nbrLayers];
                   end
              else begin
                   Vup[nbrLayers]:=V[nbrLayers]-Aim[nbrLayers]*mun;
                   Vdo[nbrLayers]:=V[nbrLayers]+Aim[nbrLayers]*mun;
                   end;
            end;
{ cas des rayons X }
            if MyExperimentalParameters.Particles='xrays' then
            begin
              { coefficient f }
              ff:=Floats[ColF,i];f[nbrLayers]:=ff;
              FitColS:=Uppercase(Cells[colFYN,i]);FitCol:=abs(Floats[colFYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin FYN[nbrLayers]:=0;fStep[nbrLayers]:=step*ff;inc(nbrParam);fCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin fYN[nbrLayers]:=0;fStep[nbrLayers]:=step*ff;inc(nbrParam);fCorr[nbrLayers]:=carac end
                   else if FitCol<>0 then begin fYN[nbrLayers]:=0;fStep[nbrLayers]:=FitCol;inc(nbrParam);fCorr[nbrLayers]:='0' end
                                     else begin fYN[nbrLayers]:=-1;fStep[nbrLayers]:=0;fCorr[nbrLayers]:='0' end;
              { coefficient fi }
              fifi:=Floats[ColFi,i];fi[nbrLayers]:=fifi;
              FitColS:=Uppercase(Cells[colFiYN,i]);FitCol:=abs(Floats[colFiYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin FiYN[nbrLayers]:=0;fiStep[nbrLayers]:=step*fifi;inc(nbrParam);fiCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin fiYN[nbrLayers]:=0;fiStep[nbrLayers]:=step*fifi;inc(nbrParam);fiCorr[nbrLayers]:=carac end
                   else if FitCol<>0 then begin fiYN[nbrLayers]:=0;fiStep[nbrLayers]:=FitCol;inc(nbrParam);fiCorr[nbrLayers]:='0' end
                                     else begin fiYN[nbrLayers]:=-1;fiStep[nbrLayers]:=0;fiCorr[nbrLayers]:='0' end;
              { calcul de delta et beta }
              delta := re * lambda * lambda  / (2*pi) * dd * ff;
              beta := re * lambda * lambda  / (2*pi) * dd * fifi;
              V[nbrLayers]:=hb2*4*pi*pi/(mn*lambda*lambda)*delta; { partie réelle du potentiel }
              Vi[nbrLayers]:=-hb2*4*pi*pi/(mn*lambda*lambda)*beta; { les signes de V et Vi doivent etre opposes n² = 1 - delta + i beta}
            end;
{ paramètres communs aux X et aux neutrons }
              { paramètres de rugosité }
              sig:=Floats[ColSigma,i]*1e-9;sigma[nbrLayers]:=sig;
              FitColS:=Uppercase(cells[colSigYN,i]);FitCol:=abs(Floats[colSigYN,i]);if FitColS<>'' then carac:=FitColS[1] else carac:='0';
              if FitColS='Y'
              then begin SigYN[nbrLayers]:=0;SigmaStep[nbrLayers]:=step*sig;inc(nbrParam);sigmaCorr[nbrLayers]:=carac end
              else if ((ord(carac)>=65) and (ord(carac)<=70)) or (carac='L')
                   then begin sigYN[nbrLayers]:=0;sigmaStep[nbrLayers]:=step*sig;inc(nbrParam);sigmaCorr[nbrLayers]:=carac end
                   else if FitCol<>0 then begin SigYN[nbrLayers]:=0;SigmaStep[nbrLayers]:=FitCol*1e-9;inc(nbrParam);sigmaCorr[nbrLayers]:='0' end
                                     else begin SigYN[nbrLayers]:=-1;SigmaStep[nbrLayers]:=0;sigmaCorr[nbrLayers]:='0' end;
              xsixsi:=Floats[ColXsi,i]*1e-9;xsi[nbrLayers]:=xsixsi;
              {FitColS:=Uppercase(TextRC[i,colxsiYN]);FitCol:=abs(NumberRC[i,colXsiYN]);
              if FitColS='Y' then begin XsiYN[nbrLayers]:=true;XsiStep[nbrLayers]:=step*xsixsi;inc(nbrParam);end
                             else if FitCol<>0 then begin XsiYN[nbrLayers]:=true;XsiStep[nbrLayers]:=FitCol*1e-9;inc(nbrParam);end
                                               else XsiYN[nbrLayers]:=false;}
              exphexph:=Floats[Colh,i];exph[nbrLayers]:=exphexph;

              { test si la couche fait partie d'un motif multicouche }
              { il n'y a pas de paramètre a ajuster }
              if Uppercase(Cells[colMLYN,i])='Y' then begin MLYN[nbrLayers]:=0 end else MLYN[nbrLayers]:=-1;
              { on teste s'il s'agit d'une couche faisant partie de la ML }
              { PROFILD }
              profile[nbrLayers]:=ints[colProfile,i];
            end;
            nbrLayers:=nbrLayers+1;
         end
         else begin
              With MyGrid do
              begin colors[ColYN,i]:=clWhite;
                    colors[ColDescription,i]:=clWhite;
                    Colors[ColEpaisseurYN,i]:=clWhite;Colors[ColDensityYN,i]:=clWhite;
                    Colors[ColBYN,i]:=clWhite;Colors[ColBiYN,i]:=clWhite;
                    Colors[ColMYN,i]:=clWhite;Colors[ColThetaYN,i]:=clWhite;Colors[ColPhiYN,i]:=clWhite;
                    Colors[ColSigYN,i]:=clWhite;Colors[ColXsiYN,i]:=clWhite;Colors[ColhYN,i]:=clWhite;
              end;
              end;
         inc(i);
    end;
    z.size(nbrLayers,false);
    bn.size(nbrLayers,false);bni.size(nbrLayers,false);
    f.size(nbrLayers,false);fi.size(nbrLayers,false);
    d.size(nbrLayers,false);
    Aim.size(nbrLayers,false);theta.size(nbrLayers,false);phi.size(nbrLayers,false);
    BeffMag.size(nbrLayers,false);BeffTheta.size(nbrLayers,false);BeffPhi.size(nbrLayers,false);
    V.size(nbrLayers,false);Vi.size(nbrLayers,false);
    Vup.size(nbrLayers,false);Vdo.size(nbrLayers,false);
    sigma.size(nbrLayers,false);xsi.size(nbrLayers,false);exph.size(nbrLayers,false);
{    nbrLayers:=nbrLayers-1; } { 26.06.02 : nbrLayers devient le nombre de couches }
    { on teste si on considère une ML }
    if MyExperimentalParameters.nbrML>1
    then begin MLSystem:=True; generateMLparameters(MyExperimentalParameters) end
    else MLSystem:=false;
    { flag PROFILED }
    profiled:=false;for i:=0 to NbrLayers-1 do if profile[i]<>0 then Profiled:=true;
end;

{==============================================================================}
{Ecrit explicitement les paramètres de toutes les couches dans une multicouche }
{INPUT: z,bn,bm,V,Vi,Vup,Vdo,theta,phi,sigma,xsi,exph                          }
{ on peut demander l'usage d'une fonction agissant sur les épaisseurs individuelles     }
{ des couches du super lattice; ces focntions doivent etre des fonctions de l'épaisseur }
{ du superlattice; voir manuel pour plus de détails                                     }
{==============================================================================}
Procedure TMultiLayer.GenerateMLParameters(var MyExpPar:TExperimentalParameters);
var i,j,k,nn,nbrCell,nbrML:integer;
    thicknessSL:double; { épaisseur de la multicouche }
begin
   thicknessSL:=0; { the thickness and roughness are functions of this parameter }
   j:=0;
   { chargement des fonctions épaisseurs et rugosité }
   dataReflecForm.parserThickness.expression:=MyExpPar.thicknessFunction;
   dataReflecForm.parserRoughness.expression:=MyExpPar.roughnessFunction;
   dataReflecForm.parserMagnetization.expression:=MyExpPar.magnetizationFunction;
   dataReflecForm.parserPhi.expression:=MyExpPar.phiFunction;
   { determination du nombre de lignes dans le tableau }
   zML.size(nbrLayersMax,false);
   bnML.size(nbrLayersMax,false);bniML.size(nbrLayersMax,false);
   fML.size(nbrLayersMax,false);fiML.size(nbrLayersMax,false);
   dML.size(nbrLayersMax,false);
   AimML.size(nbrLayersMax,false);thetaML.size(nbrLayersMax,false);phiML.size(nbrLayersMax,false);
   sigmaML.size(nbrLayersMax,false);xsiML.size(nbrLayersMax,false);exphML.size(nbrLayersMax,false);
   VML.Size(nbrlayersmax,false);ViML.Size(nbrlayersmax,false);
   VUpML.Size(nbrlayersmax,false);VDoML.Size(nbrlayersmax,false);
   BeffMagML.size(nbrLayersMax,false);
   BeffThetaML.size(nbrLayersMax,false);
   BeffPhiML.size(nbrLayersMax,false);

   j:=0;nbrcell:=0;
   for i:=0 to nbrlayers-1 do
   begin
      if MLYN[i]=-1
      then begin
        zML[j]:=z[i];dML[j]:=d[i];bnML[j]:=bn[i];bniML[j]:=bni[i];fML[j]:=f[i];fiML[j]:=fi[i];
        AimML[j]:=Aim[i];thetaML[j]:=theta[i];phiML[j]:=phi[i];sigmaML[j]:=sigma[i];xsiML[j]:=xsi[i];exphML[j]:=exph[i];
        VML[j]:=V[i];ViML[j]:=Vi[i];VupML[j]:=Vup[i];VdoML[j]:=Vdo[i];
        BeffMagML[j]:=BeffMag[i];BeffThetaML[j]:=BeffTheta[i];BeffPhiML[j]:=BeffPhi[i];
        inc(j);
        end
      else begin
        if nbrcell<=1 then { indice qui permet de scanner la ML }
        begin
        nn:=i; { indice de la première couche de la ML }
        { on initialise l'épaisseur du SL à zero }
        thicknessSL:=0;
        nbrcell:=1;
        for k:=1 to MyExpPar.nbrML do
        begin
           while MLYN[nn]=0 do
           begin
              zML[j]:=z[nn]; { lecture de l'épaisseur nominale }
              if MyExpPar.UseThicknessFunction=true
              then begin
                   dataReflecForm.parserThickness.Z:=(thicknessSL+zML[j])*1e9;
                   zML[j]:=zML[j]+dataReflecForm.parserThickness.value*1e-9;
                   end;
              thicknessSL:=thicknessSL+zML[j]; { mise a jour de la vraie épaisseur, fluctuation incluse }
              dML[j]:=d[nn];bnML[j]:=bn[nn];bniML[j]:=bni[nn];fML[j]:=f[nn];fiML[j]:=fi[nn];
              AimML[j]:=Aim[nn];thetaML[j]:=theta[nn];phiML[j]:=phi[nn];
              BeffMagML[j]:=BeffMag[nn];BeffThetaML[j]:=BeffTheta[nn];BeffPhiML[j]:=BeffPhi[nn];
              VML[j]:=V[nn];ViML[j]:=Vi[nn];VupML[j]:=Vup[nn];VdoML[j]:=Vdo[nn];
              if MyExpPar.UseMagnetizationFunction=true
              then begin
                   if AImML[j]<>0 then { on applique le gradient de M que sur les couches magnétiques ! }
                   begin dataReflecForm.parserMagnetization.Z:=thicknessSL*1e9;
                         AimML[j]:=dataReflecForm.parserMagnetization.value*(mu0*muB*dML[j]);
                         VupML[j]:=VML[j]-AimML[j]*mun;VdoML[j]:=VML[j]+AimML[j]*mun; end;
                   end;
              if MyExpPar.UsePhiFunction=true
              then begin
                   if AimML[j]<>0 then { on applique le gradient de M que sur les couches magnétiques ! }
                   begin dataReflecForm.parserPhi.Z:=thicknessSL*1e9;
                         PhiML[j]:=dataReflecForm.parserPhi.value*pi/180 end;
                   end;

              sigmaML[j]:=sigma[nn];
              if MyExpPar.UseRoughnessFunction=true
              then begin
                   dataReflecForm.parserRoughness.Z:=thicknessSL*1e9;
                   sigmaML[j]:=dataReflecForm.parserRoughness.value*1e-9;
                   end;
              xsiML[j]:=xsi[nn];exphML[j]:=exph[nn];
              inc(j);inc(nn);inc(nbrcell);
           end; { end while }
           nn:=i;
        end; { end for k }
        end; { end if nbrCell }
      end; { end else }
  end; { end for i }
  nbrML:=j;

  zML.size(nbrML,false);
  bnML.size(nbrML,false);bniML.size(nbrML,false);
  fML.size(nbrML,false);fiML.size(nbrML,false);
  dML.size(nbrML,false);
  AimML.size(nbrML,false);thetaML.size(nbrML,false);phiML.size(nbrML,false);
  sigmaML.size(nbrML,false);xsiML.size(nbrML,false);exphML.size(nbrML,false);
  VML.Size(nbrML,false);ViML.Size(nbrML,false);
  VUpML.Size(nbrML,false);VDoML.Size(nbrML,false);
  BeffMagML.size(nbrML,false);BeffThetaML.size(nbrML,false);BeffPhiML.size(nbrML,false);
end;

{ procedure qui ajoute la ponderation dans le cas de profil de type 'smooth' }
{ INPUT : une multilayer et un profil                                           }
{ OUTPUT : une pondération supplémentaire due au profil                         }
{          pas de règle mathématique; la pondération est empirique              }
function TMultiLayer.SmoothProfile(var MyProfiles:TProfiles):double;
var i:integer;
    diff:double;
    param:string;
    pond:double;
begin
   diff:=0;
   for i:=0 to nbrLayers-2 do { nbrLayers-2 parce qu'on ne met pas de profil dans le milieu d'incidence }
   begin { on travaille sur deux couches de profil adjacentes appartnant au meme profil }
      if (Profile[i]=Profile[i+1]) and (MyProfiles.profileType[Profile[i]]='smooth')
      then begin { il faut renormaliser les parametres }
           param:=MyProfiles.parameter[Profile[i]];
           pond:=MyProfiles.ProfPara[Profile[i]];
             if param='rho' then diff:=diff+pond*abs(d[i]-d[i+1])*1e-28;
             if param='bn'  then diff:=diff+pond*abs(bn[i]-bn[i+1])*1e15;
             if param='bni' then diff:=diff+pond*abs(bni[i]-bni[i+1])*1e15;
             if param='f' then diff:=diff+pond*abs(f[i]-f[i+1]);
             if param='fi' then diff:=diff+pond*abs(fi[i]-fi[i+1]);
             if param='M' then diff:=diff+pond*Aim[i]/(mu0*muB*d[i]);
             if param='theta' then diff:=diff+pond*theta[i]*180/pi;
             if param='phi' then diff:=diff+pond*phi[i]*180/pi;
           end;
   end;
   result:=diff;
end;

{ mise a jour des paramètres corrélés }
{ INPUT : CorrNr : caractère indiquant l'index de la corrélation }
{         para, string indiquent le paramètre corrélé, 'z', 'rho', 'b', 'bi', 'f', 'fi', 'M', 'theta', 'phi', 'sigma' }
{ OUTPUT : les paramètres de la ML sont mis a jour avec cette valeur                                                }
Procedure TMultiLayer.SetCorrParams(var CorrNr:char;para:string;valeur:double);
var i,NrLayers:integer;
begin
   begin
   nrLayers:=nbrLayers-1;
   if para='z' then for i:=0 to nrLayers do if zCorr[i]=CorrNr then z[i]:=valeur;
   if para='rho_n' then for i:=0 to nrLayers do if dCorr[i]=CorrNr then begin d[i]:=valeur;V[i]:=Pihm*d[i]*bn[i];Vi[i]:=-Pihm*d[i]*bni[i];Vup[i]:=V[i]-Aim[i]*mun;Vdo[i]:=V[i]+Aim[i]*mun; end;
   if para='rho_x' then for i:=0 to nrLayers do if dCorr[i]=CorrNr then begin d[i]:=valeur;V[i]:=Cte*d[i]*f[i];Vi[i]:=-Cte*d[i]*fi[i]; end;
   if para='b' then for i:=0 to nrLayers do if bCorr[i]=CorrNr then begin bn[i]:=valeur;V[i]:=Pihm*d[i]*bn[i];Vi[i]:=-Pihm*d[i]*bni[i];Vup[i]:=V[i]-Aim[i]*mun;Vdo[i]:=V[i]+Aim[i]*mun; end;
   if para='bi' then for i:=0 to nrLayers do if biCorr[i]=CorrNr then begin bni[i]:=valeur;V[i]:=Pihm*d[i]*bn[i];Vi[i]:=-Pihm*d[i]*bni[i];Vup[i]:=V[i]-Aim[i]*mun;Vdo[i]:=V[i]+Aim[i]*mun; end;
   if para='M' then for i:=0 to nrLayers do if MCorr[i]=CorrNr then begin Aim[i]:=valeur;Vup[i]:=V[i]-Aim[i]*mun;Vdo[i]:=V[i]+Aim[i]*mun end;
   if para='theta' then for i:=0 to nrLayers do if thetaCorr[i]=CorrNr then theta[i]:=valeur;
   if para='phi' then for i:=0 to nrLayers do if phiCorr[i]=CorrNr then phi[i]:=valeur;
   if para='f' then for i:=0 to nrLayers do if fCorr[i]=CorrNr then begin f[i]:=valeur;V[i]:=Cte*d[i]*f[i] end;
   if para='fi' then for i:=0 to nrLayers do if fiCorr[i]=CorrNr then begin fi[i]:=valeur;Vi[i]:=-Cte*d[i]*fi[i] end;
   if para='sigma' then for i:=0 to nrLayers do if sigmaCorr[i]=CorrNr then sigma[i]:=valeur;
   end;
end;


{ ============================================================================= }
{ gestion du formulaire associé à DataReflec                                    }
{ ============================================================================= }
procedure TdataReflecForm.FormCreate(Sender: TObject);
begin
{ reservation de la memoire pour les vecteurs et matrices }
{  SetVecCacheSize(100,NbrPointsMax);
  SetMtxCacheSize(10,10);   { attention! data complexes }
{ création des variables de stockage des data }
{  MyDataExp:=TDataReflectivity.create;
  MyDataCalc:=TDataReflectivity.create;
  MyMultiLayer:=TMultiLayer.Create;
  MyExperimentalParameters:=TExperimentalParameters.Create; }
{  MultiLayerGrid.Navigation.AllowDeleteRow:=true;
  MultiLayerGrid.Navigation.AllowInsertRow:=true; }
  WindowState:=wsMinimized;  
end;

{ inusité }
procedure TDataReflecForm.InitDefaultGrid(MyGrid:TAdvStringGrid;Particles:String);
begin
    { initialisation des couches par defaut (si aucun fichier n'est disponible) }
    { substrat }
    With MyGrid do
    begin
        { ligne substrat }
        Cells[ColYN,1]:='Y';
        Cells[ColDescription,1]:='Substrat';
        Floats[ColEpaisseur,1]:=0;Cells[ColEpaisseurYN,1]:='N';
        Floats[ColDensity,1]:=5;Cells[ColDensityYN,1]:='N';
        Floats[ColB,1]:=4.15;Cells[ColBYN,1]:='N';
        Floats[ColBi,1]:=0;Cells[ColBiYN,1]:='N';
        Floats[ColM,1]:=0;Cells[ColMYN,1]:='N';
        Floats[ColTheta,1]:=0;Cells[ColThetaYN,1]:='N';
        Floats[ColPhi,1]:=0;Cells[ColPhiYN,1]:='N';
        Floats[ColSigma,1]:=0;Cells[ColSigYN,1]:='N';
        Floats[ColXsi,1]:=0;
        Floats[Colh,1]:=0;
        { couche de nickel }
        Cells[ColYN,2]:='Y';
        Cells[ColDescription,2]:='Nickel';
        Floats[ColEpaisseur,2]:=40;Cells[ColEpaisseurYN,2]:='N';
        Floats[ColDensity,2]:=9;Cells[ColDensityYN,2]:='N';
        Floats[ColB,2]:=10.3;Cells[ColBYN,2]:='N';
        Floats[ColBi,2]:=0;Cells[ColBiYN,2]:='N';
        Floats[ColM,2]:=0.6;Cells[ColMYN,2]:='N';
        Floats[ColTheta,2]:=90;Cells[ColThetaYN,2]:='N';
        Floats[ColPhi,2]:=0;Cells[ColPhiYN,2]:='N';
        Floats[ColSigma,2]:=0;Cells[ColSigYN,2]:='N';
        Floats[ColXsi,2]:=0;
        Floats[Colh,2]:=0;
        { milieu d'incidence, par defaut vide }
        Cells[ColYN,3]:='Y';
        Cells[ColDescription,3]:='Vide';
        Floats[ColEpaisseur,3]:=0;Cells[ColEpaisseurYN,3]:='N';
        Floats[ColDensity,3]:=9;Cells[ColDensityYN,3]:='N';
        Floats[ColB,3]:=10.3;Cells[ColBYN,3]:='N';
        Floats[ColBi,3]:=0;Cells[ColBiYN,3]:='N';
        Floats[ColM,3]:=0.6;Cells[ColMYN,3]:='N';
        Floats[ColTheta,3]:=90;Cells[ColThetaYN,3]:='N';
        Floats[ColPhi,3]:=0;Cells[ColPhiYN,3]:='N';
        Floats[ColSigma,3]:=0;Cells[ColSigYN,3]:='N';
        Floats[ColXsi,3]:=0;
        Floats[Colh,3]:=0;
   end;
end;

{ ============================================================================= }
{ méthodes associées à la classe TProfiles                                      }
{ ============================================================================= }
constructor TProfiles.Create;
var i:integer;
begin
   inherited Create;
   NrOfProfiles:=0;
   for i:=1 to NbrProfilesMax do YN[i]:=false;
end;

{ mise a jour d'une variable TProfiles a partir d'une StringGrid }
procedure TProfiles.update(ProfilesGrid:TAdvStringGrid);
var i:integer;
begin
   NrOfProfiles:=0;ProfiledLayers:=[];
   for i:=1 to NbrProfilesMax do
   begin
     YN[i]:=false;
     if ProfilesGrid.Cells[ColPYN,i]='Y'
     then with ProfilesGrid do
          begin
               inc(NrOfProfiles);
               YN[NrOfProfiles]:=true;
               description[NrOfProfiles]:=Cells[ColPDescription,i];
               LayerNr[NrOfProfiles]:=Ints[ColPLayerNr,i];
               Parameter[NrOfProfiles]:=Cells[ColPParameter,i];
               ProfileType[NrOfProfiles]:=Cells[ColPProfileType,i];
               NrOfSubLayers[NrOfProfiles]:=Ints[ColPNrOfSublayers,i];
               Bottom[NrOfProfiles]:=Floats[ColPBottom,i];
               Topp[NrOfProfiles]:=Floats[ColPTop,i];
               ProfPara[NrOfProfiles]:=Floats[ColPProfPara,i];
               ProfiledLayers:=ProfiledLayers+[LayerNr[NrOfProfiles]];
            end;
   end;
end;


end.





