Beispiel #1
0
 public IonSet(List <Ion> ions)
 {
     foreach (Ion sourceIon in ions)
     {
         Ion copiedIon = new Ion(sourceIon);
         this.ions.Add(copiedIon);
     }
 }
Beispiel #2
0
 public void equations(double[] x, double[] f)
 {
     Calculate.Ljp(list, x, f, temperatureC);
     for (int j = 0; j < ionCount - 2; j++)
     {
         Ion ion = list[j];
         f[j] = (f[j] - ion.cL) / sigma[j];
     }
 }
Beispiel #3
0
 public Ion(Ion ion, double c0, double cL)
 {
     name         = ion.name;
     charge       = ion.charge;
     conductivity = ion.conductivity;
     mu           = ion.mu;
     this.c0      = c0;
     this.cL      = cL;
 }
Beispiel #4
0
 public Ion(Ion ion)
 {
     this.name         = ion.name;
     this.charge       = ion.charge;
     this.conductivity = ion.conductivity;
     this.mu           = ion.mu;
     this.c0           = ion.c0;
     this.cL           = ion.cL;
     this.phi          = ion.phi;
 }
Beispiel #5
0
        private Ion IonFromMarkdownLine(String line)
        {
            const double K_CONDUCTANCE = 73.5;

            line = line.Trim();

            var  parts              = line.Split('|');
            bool isThreeItems       = (parts.Length == 3);
            bool isFiveItems        = (parts.Length == 5);
            bool isThreeOrFiveItems = isThreeItems || isFiveItems;

            if (isThreeOrFiveItems == false)
            {
                return(null);
            }

            if (parts[1].StartsWith("---") || parts[1].Contains("Charge"))
            {
                return(null);
            }

            string strCond = parts[2];

            strCond = strCond.Replace(" ", "").ToUpper();
            bool isNormalizedToK = false;

            if (strCond.Contains("*K"))
            {
                strCond         = strCond.Replace("*K", "");
                isNormalizedToK = true;
            }

            try
            {
                string name        = parts[0].Trim();
                int    charge      = int.Parse(parts[1]);
                double conductance = double.Parse(strCond) * 1E-4;
                if (isNormalizedToK)
                {
                    conductance *= K_CONDUCTANCE;
                }

                double c0 = (parts.Length == 5) ? double.Parse(parts[3]) : 0;
                double cL = (parts.Length == 5) ? double.Parse(parts[4]) : 0;

                Ion ion = new Ion(name, charge, conductance, c0, cL);
                Debug.WriteLine($"Parsed ion: {ion}");
                return(ion);
            }
            catch
            {
                Debug.WriteLine($"Could not parse line: {line}");
                return(null);
            }
        }
Beispiel #6
0
        public Ion Lookup(Ion ion)
        {
            foreach (Ion tableIon in ions)
            {
                if (string.Compare(ion.name, tableIon.name, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    return(new Ion(tableIon.name, tableIon.charge, tableIon.conductivity, ion.c0, ion.cL));
                }
            }

            return(new Ion(ion));
        }
Beispiel #7
0
        public PhiEquations(List <Ion> list, double temperatureC)
        {
            this.list = list;
            ionCount  = list.Count;

            this.temperatureC = temperatureC;

            // determine smallest nonzero cL of all ions
            double smallestCL = Double.PositiveInfinity;

            for (int j = 0; j < ionCount; j++)
            {
                Ion  ion      = list[j];
                bool ionHasCL = (ion.cL > 0);
                if (ionHasCL)
                {
                    double thisCl = Math.Abs(ion.cL);
                    smallestCL = Math.Min(smallestCL, thisCl);
                }
            }

            // set sigmas to cLs (unless cL is zero, then use smallest nonzero cL)
            sigma = new double[ionCount];
            for (int j = 0; j < ionCount; j++)
            {
                Ion  ion      = list[j];
                bool ionHasCL = (ion.cL > 0);
                if (ionHasCL)
                {
                    sigma[j] = 0.01 * Math.Abs(ion.cL);
                }
                else
                {
                    sigma[j] = 0.01 * smallestCL;
                }
            }
        }
Beispiel #8
0
        public void Load(string filePath, bool ignoreDuplicates = false, bool sort = false)
        {
            ions.Clear();
            foreach (string line in System.IO.File.ReadAllLines(filePath))
            {
                Ion ion = IonFromMarkdownLine(line);
                if (ion != null)
                {
                    if (ignoreDuplicates && Contains(ion.name))
                    {
                        continue;
                    }

                    ions.Add(ion);
                }
            }

            if (sort)
            {
                var sortedIons = ions.OrderBy(ion => ion.name).ToList();
                ions.Clear();
                ions.AddRange(sortedIons);
            }
        }
Beispiel #9
0
        // This function is balanced by the equation solver, not called directly by the user.
        public static double Ljp(List <Ion> ionList, double[] startingPhis, double[] startingCLs, double temperatureC)
        {
            double KT = Constants.boltzmann * (temperatureC + Constants.zeroCinK);

            double cdadc = 1.0; // fine for low concentrations

            int ionCount               = ionList.Count;
            int ionCountMinusOne       = ionCount - 1;
            int ionCountMinusTwo       = ionCount - 2;
            int indexLastIon           = ionCount - 1;
            int indexSecondFromLastIon = ionCount - 2;

            Ion lastIon           = ionList[indexLastIon];
            Ion secondFromLastIon = ionList[indexSecondFromLastIon];

            if (startingPhis.Length != ionCount - 2)
            {
                throw new ArgumentException();
            }

            if (startingCLs.Length != ionCount - 2)
            {
                throw new ArgumentException();
            }

            // populate charges, mus, and rhos from all ions except the last one
            double[] charges = new double[ionCountMinusOne];
            double[] mus     = new double[ionCountMinusOne];
            double[] rhos    = new double[ionCountMinusOne];
            for (int j = 0; j < ionCountMinusOne; j++)
            {
                Ion ion = ionList[j];
                charges[j] = ion.charge;
                mus[j]     = ion.mu;
                rhos[j]    = ion.c0;
            }

            // populate phis from all ions except the last two
            double[] phis = new double[ionCountMinusOne];
            for (int j = 0; j < ionCountMinusTwo; j++)
            {
                phis[j] = startingPhis[j];
            }

            // second from last phi is the concentration difference
            phis[indexSecondFromLastIon] = secondFromLastIon.cL - secondFromLastIon.c0;

            // prepare info about second from last ion concentration difference for loop
            if (secondFromLastIon.c0 == secondFromLastIon.cL)
            {
                throw new InvalidOperationException("second from last ion concentrations cannot be equal");
            }

            double KC0 = secondFromLastIon.c0;
            double KCL = secondFromLastIon.cL;
            double dK  = (KCL - KC0) / 1000.0;

            // set last ion C0 based on charges, rhos, and linear algebra
            double zCl   = ionList[indexLastIon].charge;
            double rhoCl = -Linalg.ScalarProduct(charges, rhos) / zCl;

            lastIon.c0 = rhoCl;

            // cycle to determine junction voltage

            double V = 0.0;

            for (double rhoK = KC0; ((dK > 0) ? rhoK <= KCL : rhoK >= KCL); rhoK += dK)
            {
                rhoCl = -Linalg.ScalarProduct(rhos, charges) / zCl;

                double DCl = lastIon.mu * KT * cdadc;
                double vCl = zCl * Constants.e * lastIon.mu * rhoCl;

                double[] v = new double[ionCountMinusOne];
                double[,] mD = new double[ionCountMinusOne, ionCountMinusOne];

                for (int j = 0; j < ionCountMinusOne; j++)
                {
                    for (int k = 0; k < ionCountMinusOne; k++)
                    {
                        mD[j, k] = 0.0;
                    }
                }

                for (int j = 0; j < ionCountMinusOne; j++)
                {
                    for (int k = 0; k < ionCountMinusOne; k++)
                    {
                        mD[j, k] = 0.0;
                    }
                    mD[j, j] = mus[j] * KT * cdadc;
                    v[j]     = charges[j] * Constants.e * mus[j] * rhos[j];
                }

                if (Linalg.ScalarProduct(charges, v) + zCl * vCl == 0.0)
                {
                    return(Double.NaN); // Singularity; abort calculation
                }

                double[,] identity  = Linalg.Identity(ionCountMinusOne);
                double[,] linAlgSum = Linalg.Sum(1, mD, -DCl, identity);
                double[] sumCharge         = Linalg.Product(linAlgSum, charges);
                double   chargesProd       = Linalg.ScalarProduct(charges, v);
                double   chargesProdPlusCl = chargesProd + zCl * vCl;
                double[] delta             = Linalg.ScalarMultiply(sumCharge, 1.0 / chargesProdPlusCl);

                double[,] mDyadic = new double[ionCountMinusOne, ionCountMinusOne];
                for (int j = 0; j < ionCountMinusOne; j++)
                {
                    for (int k = 0; k < ionCountMinusOne; k++)
                    {
                        mDyadic[j, k] = v[j] * delta[k];
                    }
                }
                double[,] mM = Linalg.Sum(1, mDyadic, -1, mD);

                double[] rhop  = Linalg.Solve(mM, phis);
                double   rhopK = rhop[indexSecondFromLastIon];
                rhos = Linalg.Sum(1, rhos, dK / rhopK, rhop);

                double E = Linalg.ScalarProduct(delta, rhop);
                V -= E * dK / rhopK;
            }

            // modify CLs based on the rhos we calculated
            for (int j = 0; j < ionCountMinusTwo; j++)
            {
                startingCLs[j] = rhos[j];
            }

            return(V);
        }
Beispiel #10
0
        public static LjpResult Ljp(List <Ion> ionList, double temperatureC = 25)
        {
            foreach (Ion ion in ionList)
            {
                if (ion.charge == 0)
                {
                    throw new ArgumentException("ion charge cannot be zero");
                }
                if (ion.mu == 0)
                {
                    throw new ArgumentException("ion mu cannot be zero");
                }
            }

            LjpResult result = new LjpResult(ionList, temperatureC);

            int ionCount         = ionList.Count;
            int ionCountMinusOne = ionCount - 1;
            int ionCountMinusTwo = ionCount - 2;

            Ion secondFromLastIon = ionList[ionCount - 2];
            Ion LastIon           = ionList[ionCount - 1];

            if (secondFromLastIon.c0 == secondFromLastIon.cL)
            {
                throw new InvalidOperationException("second from last ion concentrations cannot be equal");
            }

            // phis will be solved for all ions except the last two
            double[] phis = new double[ionCountMinusTwo];

            // phis to solve are initialized to the concentration difference
            for (int j = 0; j < phis.Length; j++)
            {
                Ion ion = ionList[j];
                phis[j] = ion.cL - ion.c0;
            }

            // all phis except the last two get solved
            if (ionCount > 2)
            {
                var    phiEquations = new PhiEquations(ionList, temperatureC) as IEquationSystem;
                Solver s            = new Solver(phiEquations);
                s.solve(phis);
            }

            // calculate LJP
            double[] cLs   = new double[phis.Length];
            double   ljp_V = Ljp(ionList, phis, cLs, temperatureC);

            if (ljp_V == Double.NaN)
            {
                throw new Exception("ERROR: Singularity (calculation aborted)");
            }

            // update ions based on what was just calculated
            for (int j = 0; j < phis.Length; j++)
            {
                Ion ion = ionList[j];
                ion.phi = phis[j];
                ion.cL  = cLs[j];
            }

            // second from last ion phi is concentration difference
            secondFromLastIon.phi = secondFromLastIon.cL - secondFromLastIon.c0;

            // last ion's phi is calculated from all the phis before it
            double totalChargeWeightedPhi = 0.0;

            for (int j = 0; j < ionCountMinusOne; j++)
            {
                Ion ion = ionList[j];
                totalChargeWeightedPhi += ion.phi * ion.charge;
            }
            LastIon.phi = -totalChargeWeightedPhi / LastIon.charge;

            // last ion's cL is calculated from all the cLs before it
            double totalChargeWeightedCL = 0.0;

            for (int j = 0; j < ionCountMinusOne; j++)
            {
                Ion ion = ionList[j];
                totalChargeWeightedCL += ion.cL * ion.charge;
            }
            LastIon.cL = -totalChargeWeightedCL / LastIon.charge;

            result.Finished(ionList, ljp_V);
            return(result);
        }