package ppmrmn;

import java.util.Observable;

/*****************************************************************************
*                                                                            *
*                             model  MQMASpowderModel                        *
*                                                                            *
*****************************************************************************/
public class MQMASpowderModel extends Observable {

    private int      nbou,         //number of increment of pulse duration
                     matRow,       //row and column of the selected mulitple-
                     matColumn,      //quantum coherence in the density matrix
                     rowCT,        //central-transition row in the density matrix
                     maxAlpha,      //for division of Euler angle alpha
                     maxBeta,       //for division of Euler angle beta
                     maxGamma,      //for division of Euler angle gamma
                     progr,         //for progressBar
                     valueEkoAntieko, //0 if echo, 1 if antiecho
                     valueSpin;       //0 if spin 3/2
    private char     coherenceChoice; //for coherence generated by the 1st pulse
    private boolean  stopRun;         //for button Run
    private double   coeff,               //for signal normalisation use
                     pop,                 //for signal normalisation use
                     initialPulseDuration,  //inital duration of the variable pulse
                     initialPulseDuration1, //duration of the constant pulse-1
                     initialPulseDuration2, //duration of the constant pulse-2
                     pulseIncrement,        //increment of the pulse duration
                     finalPulseDuration,    //final duration of the variable pulse
                     asymmetryParameter,
                     spinningSpeed,     //rotor spinning speed in kHz unit
                     omegaRFkHz,        //RF field in kHz unit
                     qcckHz;            //quadrupole coupling constant in kHz unit
    private double[] amplitude;         //FID intensity

    private Nmr myNmr;

    public MQMASpowderModel() {
    }//end of constructor

    //---------------------//
    // Setters and getters //
    //---------------------//
    public void   setvalueEkoAntieko(int x) {valueEkoAntieko = x;}
    public void   setvalueSpin(int x) {valueSpin = x;}

    public int    getprogr() {return progr;} //for progressBar

    public void   setMaxAlpha(int x)   {maxAlpha = x;}
    public int    getMaxAlpha() {return maxAlpha;} //for progressBar

    public void   setMaxBeta(int x)  {maxBeta = x;}
    public void   setMaxGamma(int x) {maxGamma = x;}

    public void   setnbou(int x)   {nbou = x;}
    public int    getnbou() {return nbou;}

    public void   setcoherenceChoice(char x)  {coherenceChoice = x;}
    public char   getcoherenceChoice() {return coherenceChoice;}

    public void    setstopRun(boolean x) {stopRun = x;}
    public boolean getstopRun()   {return stopRun;}

    public void   setcoeff(double x) {coeff = x;}
    public double getcoeff()  {return coeff;}

    public void   setpop(double x) {pop = x;}
    public double getpop()  {return pop;}

    public void   setinitialPulseDuration(double x) {initialPulseDuration = x;}
    public double getinitialPulseDuration()  {return initialPulseDuration;}

    public void   setinitialPulseDuration1(double x) {initialPulseDuration1 = x;}
    public double getinitialPulseDuration1()  {return initialPulseDuration1;}

    public void   setinitialPulseDuration2(double x) {initialPulseDuration2 = x;}
    public double getinitialPulseDuration2()  {return initialPulseDuration2;}

    public void   setpulseIncrement(double x) {pulseIncrement = x;}
    public double getpulseIncrement()  {return pulseIncrement;}

    public void   setfinalPulseDuration(double x) {finalPulseDuration = x;}
    public double getfinalPulseDuration()  {return finalPulseDuration;}

    public void   setasymmetryParameter(double x) {asymmetryParameter = x;}

    public void   setspinningSpeed(double x) {spinningSpeed = x;}

    public void   setomegaRFkHz(double x) {omegaRFkHz = x;}
    public double getomegaRFkHz()  {return omegaRFkHz;}

    public void   setqcckHz(double x) {qcckHz = x;}
    public double getqcckHz()  {return qcckHz;}

    private void    setamplitude(double[] x) {amplitude = x;}
    public double[] getamplitude()    {return amplitude;}

    //-------------------//
    //normalisationSignal//
    //-------------------//
    private void normalisationSignal() {
      switch (valueEkoAntieko) {
        //echo
        case 0: switch (valueSpin) {
                   case 0: ComplexMat.setMatrixSize(4);
                           rowCT = 2;  coeff = 5.0;  pop = Math.sqrt(4);
                           switch (coherenceChoice) {
                             case '1': matRow = 1;   matColumn = 2;  break;
                             case '3': matRow = 3;   matColumn = 0;  break;
                           }
                           myNmr = new Spin3Half(qcckHz, omegaRFkHz);
                           //qcckHz and omegaRFkHz in kHz become
                           //OmegaQ (= qcc) and omegaRF in rad/s
                           break;
                   case 1: ComplexMat.setMatrixSize(6);
                           rowCT = 3;  coeff = 35.0/2.0;  pop = Math.sqrt(9);
                           switch (coherenceChoice) {
                             case '1': matRow = 2;   matColumn = 3;  break;
                             case '3': matRow = 1;   matColumn = 4;  break;
                             case '5': matRow = 5;   matColumn = 0;  break;
                           }
                           myNmr = new Spin5Half(qcckHz, omegaRFkHz);
                           //qcckHz and omegaRFkHz in kHz become
                           //OmegaQ (= qcc) and omegaRF in rad/s
                           break;
                   case 2: ComplexMat.setMatrixSize(8);
                           rowCT = 4;  coeff = 42.0;  pop = Math.sqrt(16);
                           switch (coherenceChoice) {
                             case '1': matRow = 3;   matColumn = 4;  break;
                             case '3': matRow = 2;   matColumn = 5;  break;
                             case '5': matRow = 1;   matColumn = 6;  break;
                             case '7': matRow = 7;   matColumn = 0;  break;
                           }
                           myNmr = new Spin7Half(qcckHz, omegaRFkHz);
                           //qcckHz and omegaRFkHz in kHz become
                           //OmegaQ (= qcc) and omegaRF in rad/s
                           break;
                   case 3: ComplexMat.setMatrixSize(10);
                           rowCT = 5;  coeff = 165.0/2.0;  pop = Math.sqrt(25);
                           switch (coherenceChoice) {
                             case '1': matRow = 4;   matColumn = 5;  break;
                             case '3': matRow = 3;   matColumn = 6;  break;
                             case '5': matRow = 2;   matColumn = 7;  break;
                             case '7': matRow = 1;   matColumn = 8;  break;
                             case '9': matRow = 9;   matColumn = 0;  break;
                           }
                           myNmr = new Spin9Half(qcckHz, omegaRFkHz);
                           //qcckHz and omegaRFkHz in kHz become
                           //OmegaQ (= qcc) and omegaRF in rad/s
                           break;
                }//end of switch case 0
                break;
        //antiécho
        case 1: switch (valueSpin) {
                   case 0: ComplexMat.setMatrixSize(4);
                           rowCT = 2;  coeff = 5.0;  pop = Math.sqrt(4);
                           switch (coherenceChoice) {
                             case '1': matRow = 2;   matColumn = 1;  break;
                             case '3': matRow = 0;   matColumn = 3;  break;
                           }
                           myNmr = new Spin3Half(qcckHz, omegaRFkHz);
                           //qcckHz and omegaRFkHz in kHz become
                           //OmegaQ (= qcc) and omegaRF in rad/s
                           break;
                   case 1: ComplexMat.setMatrixSize(6);
                           rowCT = 3;  coeff = 35.0/2.0;  pop = Math.sqrt(9);
                           switch (coherenceChoice) {
                             case '1': matRow = 3;   matColumn = 2;  break;
                             case '3': matRow = 4;   matColumn = 1;  break;
                             case '5': matRow = 0;   matColumn = 5;  break;
                           }
                           myNmr = new Spin5Half(qcckHz, omegaRFkHz);
                           //qcckHz and omegaRFkHz in kHz become
                           //OmegaQ (= qcc) and omegaRF in rad/s
                           break;
                   case 2: ComplexMat.setMatrixSize(8);
                           rowCT = 4;  coeff = 42.0;  pop = Math.sqrt(16);
                           switch (coherenceChoice) {
                             case '1': matRow = 4;   matColumn = 3;  break;
                             case '3': matRow = 5;   matColumn = 2;  break;
                             case '5': matRow = 6;   matColumn = 1;  break;
                             case '7': matRow = 0;   matColumn = 7;  break;
                           }
                           myNmr = new Spin7Half(qcckHz, omegaRFkHz);
                           //qcckHz and omegaRFkHz in kHz become
                           //OmegaQ (= qcc) and omegaRF in rad/s
                           break;
                   case 3: ComplexMat.setMatrixSize(10);
                           rowCT = 5;  coeff = 165.0/2.0;  pop = Math.sqrt(25);
                           switch (coherenceChoice) {
                             case '1': matRow = 5;   matColumn = 4;  break;
                             case '3': matRow = 6;   matColumn = 3;  break;
                             case '5': matRow = 7;   matColumn = 2;  break;
                             case '7': matRow = 8;   matColumn = 1;  break;
                             case '9': matRow = 0;   matColumn = 9;  break;
                           }
                           myNmr = new Spin9Half(qcckHz, omegaRFkHz);
                           //qcckHz and omegaRFkHz in kHz become
                           //OmegaQ (= qcc) and omegaRF in rad/s
                           break;
                }//end of switch case 1
                break;
      }//end of switch choiceEkoAntieko
    }//end of normalisationSignal()

    //-----------------------------//
    // Sequences A(p1)B() and A(p1)//
    //-----------------------------//
    public void mqSequenceOne(int nCycle1, int nCycle2, int choiceSequence) {
      RealMat    matHD, matR;
      ComplexMat matC4, matC10;

      double alpha, beta, gamma;
      double[] amp = new double[nbou + 1];
      for (int i = 0; i <= nbou; i++) amp[i] = 0.0 ;  //clear

      normalisationSignal();
      double qcc   = myNmr.getOmegaQ(),
             spin1 = myNmr.getSpin(),
             rap   = 3.0*qcc/(8*spin1*(2*spin1 - 1));

      for (int p1 = 0; p1 < maxAlpha; p1++) {
        if (stopRun == true) break;
        alpha = 2*Math.PI*p1/maxAlpha;
        for (int p2 = 1; p2 < maxBeta; p2++) {
          beta = Math.PI*p2/maxBeta;
          if (stopRun == true) break;
          for (int p3 = 0; p3 < maxGamma; p3++) {
            gamma = 2*Math.PI*p3/maxGamma;
            if (stopRun == true) break;

            matC4 = myNmr.matrixIz(); //one Iz matrix = one single crystal
            for (int i = 0; i < nCycle1; i++) {
              if (stopRun == true) break;
              double tp1  = i*pulseIncrement;
              double tmp2 = rap*myNmr.xtalmasQuadrupole(alpha, beta, gamma,
                                spinningSpeed, asymmetryParameter, tp1);
              myNmr.setOmegaQ(tmp2);
              matHD = myNmr.hamiltonianEigenValue();
              matR  = myNmr.hamiltonianEigenVector();
              matC4 = myNmr.excitationPulse(matC4, matHD, matR, pulseIncrement);

              if (choiceSequence == 2) //A(p1)
                  amp[i+1] += Math.sin(Math.PI*p2/maxBeta)
                             *matC4.im[matRow][matColumn];
                  //with amp[0] = 0, no pulse duration, no signal
                else {  //A(p1)B()
                  matC10 = myNmr.matSingleElement(matC4, matRow, matColumn);
                  for (int j = 0; j < nCycle2; j++) {
                    double tp2  = j*pulseIncrement;
                    double tmp3 = rap*myNmr.xtalmasQuadrupole(alpha, beta, gamma,
                                  spinningSpeed, asymmetryParameter,
                                  tp1 + pulseIncrement + tp2);
                    myNmr.setOmegaQ(tmp3);
                    matHD  = myNmr.hamiltonianEigenValue();
                    matR   = myNmr.hamiltonianEigenVector();
                    matC10 = myNmr.conversionPulse(matC10, matHD, matR, pulseIncrement);
                  }//end of for j
                  amp[i+1] += Math.sin(Math.PI*p2/maxBeta)
                              *matC10.im[rowCT][rowCT - 1];
              }//end of else
            }//end of for i
          }//end of for p3
        };//end of for p2
        notifyAlphaIncrease(p1);
      };//end of for p1
      setamplitude(amp);
    }//end of mqSequenceOne()

    //-------------------//
    // Sequences A()B(p2)//
    //-------------------//
    public void mqSequenceTwo(int nCycle1, int nCycle2, int choiceSequence) {
      RealMat    matHD, matR;
      ComplexMat matC4, matC10;

      double alpha, beta, gamma;
      double[] amp = new double[nbou + 1];
      for (int i = 0; i <= nbou; i++) amp[i] = 0.0 ; //clear

      normalisationSignal();
      double qcc   = myNmr.getOmegaQ(),
             spin1 = myNmr.getSpin(),
             rap   = 3.0*qcc/(8*spin1*(2*spin1 - 1)),
             tp1   = 0;

      for (int p1 = 0; p1 < maxAlpha; p1++) {
        alpha = 2*Math.PI*p1/maxAlpha;
        if (stopRun == true) break;
        for (int p2 = 1; p2 < maxBeta; p2++) {
          beta = Math.PI*p2/maxBeta;
          if (stopRun == true) break;
          for (int p3 = 0; p3 < maxGamma; p3++) {
            gamma = 2*Math.PI*p3/maxGamma;
            if (stopRun == true) break;

            matC4 = myNmr.matrixIz();  //one Iz matrix = one single crystal
            for (int i = 0; i < nCycle1; i++) {
              if (stopRun == true) break;
              tp1 = i*pulseIncrement;
              double tmp2 = rap*myNmr.xtalmasQuadrupole(alpha, beta, gamma,
                                spinningSpeed, asymmetryParameter, tp1);
              myNmr.setOmegaQ(tmp2);
              matHD = myNmr.hamiltonianEigenValue();
              matR  = myNmr.hamiltonianEigenVector();
              matC4 = myNmr.excitationPulse(matC4, matHD, matR, pulseIncrement);
            }//end of for i

            double z1 = matC4.im[matRow][matColumn];
            //antiecho && SQ coherence
            if ((choiceSequence == 1) && (coherenceChoice == '1'))
              amp[0] += Math.sin(Math.PI*p2/maxBeta)*z1;

            matC10 = myNmr.matSingleElement(matC4, matRow, matColumn);

            for (int j = 0; j < nCycle2; j++) {
              double tp2  = j*pulseIncrement;
              double tmp3 = rap*myNmr.xtalmasQuadrupole(alpha, beta, gamma,
                            spinningSpeed, asymmetryParameter,
                            tp1 + pulseIncrement + tp2);
              myNmr.setOmegaQ(tmp3);
              matHD  = myNmr.hamiltonianEigenValue();
              matR   = myNmr.hamiltonianEigenVector();
              matC10 = myNmr.conversionPulse(matC10, matHD, matR, pulseIncrement);

              amp[j+1] += Math.sin(Math.PI*p2/maxBeta)
                               *matC10.im[rowCT][rowCT - 1];
            }//end of for j
          }//end of for p3
        };//end of for p2
        notifyAlphaIncrease(p1);
      };//end of for p1
      setamplitude(amp);
    }//end of mqSequenceTwo()

    private void notifyAlphaIncrease(int p) {
      progr = p;
      setChanged();
      notifyObservers(this);    //progressBarRun.setValue(p1 + 1);
    }//end of notifyAlphaIncrease

}//end of class MQMASpowderModel
