/**
   *
   *
   * <h2>Compute Transfer Matrix Including Space Charge</h2>
   *
   * <p>Computes the back-propagating transfer matrix over the incremental distance <code>dblLen
   * </code> for the beamline modeling element <code>ifcElem</code>, and for the given <code>probe
   * </code>. We include space charge and emittance growth effects if specified.
   *
   * <p><strong>NOTE</strong>: (CKA) <br>
   * &middot; If space charge is included, the space charge matrix is computed for length <code>
   * dblLen</code>, but at a half-step location behind the current probe position. This method is
   * the same technique used by Trace3D. The space charge matrix is then pre- and post- multiplied
   * by the element transfer matrix for a half-step before and after the mid-step position,
   * respectively. <br>
   * &middot; I do not know if this (leap-frog) technique buys us much more accuracy then full
   * stepping.
   *
   * @param dblLen incremental path length
   * @param probe beam probe under simulation
   * @param ifcElem beamline element propagating probe
   * @return transfer matrix for given element
   * @throws ModelException bubbles up from IElement#transferMap()
   * @see EnvelopeTracker#compScheffMatrix(double, EnvelopeProbe, PhaseMatrix)
   * @see EnvelopeTracker#transferEmitGrowth(EnvelopeProbe, IElement, PhaseMatrix)
   * @see EnvelopeTracker#modTransferMatrixForDisplError(double, double, double, PhaseMatrix)
   */
  private PhaseMatrix compTransferMatrix(double dblLen, EnvelopeProbe probe, IElement ifcElem)
      throws ModelException {

    // Returned value
    PhaseMatrix matPhi; // transfer matrix including all effects

    // Check for exceptional circumstance and modify transfer matrix accordingly
    if (ifcElem instanceof IdealRfGap) {
      IdealRfGap elemRfGap = (IdealRfGap) ifcElem;
      double dW = elemRfGap.energyGain(probe, dblLen);
      double W = probe.getKineticEnergy();
      probe.setKineticEnergy(W - dW);
      PhaseMatrix matPhiI = elemRfGap.transferMap(probe, dblLen).getFirstOrder();

      if (this.getEmittanceGrowth()) {
        double dphi = this.effPhaseSpread(probe, elemRfGap);

        matPhiI = super.modTransferMatrixForEmitGrowth(dphi, matPhiI);
      }
      matPhi = matPhiI.inverse();
      probe.setKineticEnergy(W);

      return matPhi;
    }

    if (dblLen == 0.0) {
      matPhi = ifcElem.transferMap(probe, dblLen).getFirstOrder();

      return matPhi;
    }

    // Check for easy case of no space charge
    if (!this.getUseSpacecharge()) {
      matPhi = ifcElem.transferMap(probe, dblLen).getFirstOrder();

      // we must treat space charge
    } else {

      // Store the current probe state (for rollback)
      EnvelopeProbeState state0 = probe.cloneCurrentProbeState();
      // ProbeState  state0 = probe.createProbeState();

      // Get half-step back-propagation matrix at current probe location
      //  NOTE: invert by computing for negative propagation length
      PhaseMap mapElem0 = ifcElem.transferMap(probe, -dblLen / 2.0);
      PhaseMatrix matPhi0 = mapElem0.getFirstOrder();

      // Get the RMS envelopes at probe location
      CovarianceMatrix covTau0 = probe.getCovariance(); // covariance matrix at entrance

      // Move probe back a half step for position-dependent transfer maps
      double pos = probe.getPosition() - dblLen / 2.0;
      PhaseMatrix matTau1 = covTau0.conjugateTrans(matPhi0);
      CovarianceMatrix covTau1 = new CovarianceMatrix(matTau1);

      probe.setPosition(pos);
      probe.setCovariance(covTau1);

      // space charge transfer matrix
      //  NOTE: invert by computing for negative propagation length
      PhaseMatrix matPhiSc = this.compScheffMatrix(-dblLen, probe, ifcElem);

      // Compute half-step transfer matrix at new probe location
      PhaseMap mapElem1 = ifcElem.transferMap(probe, -dblLen / 2.0);
      PhaseMatrix matPhi1 = mapElem1.getFirstOrder();

      // Restore original probe state
      probe.applyState(state0);

      // Compute the full transfer matrix for the distance dblLen
      matPhi = matPhi1.times(matPhiSc.times(matPhi0));
    }

    if (ifcElem instanceof IdealMagQuad) {
      // sako  put alignment error in sigma matrix
      //  NOTE the use of negative displacements for back-propagation
      IdealMagQuad elemQuad = (IdealMagQuad) ifcElem;

      double delx = -elemQuad.getAlignX();
      double dely = -elemQuad.getAlignY();
      double delz = -elemQuad.getAlignZ();

      matPhi = this.modTransferMatrixForDisplError(delx, dely, delz, matPhi);
    }

    return matPhi;
  }