コード例 #1
0
    /// <summary>
    /// Solves for LJP (which calculates new CLs) and returns the percent error for each of the new CLs.
    /// A solution is found when all CLs used to calculate LJP are sufficiently close to those defined in the ion table.
    /// Typically a solution is one where all CL errors are less than 1%.
    /// </summary>
    public PhiEquationSolution Calculate(double[] phis)
    {
        double[] originalCLs = Enumerable.Range(0, Ions.Length)
                               .Select(x => Ions[x].CL)
                               .ToArray();

        (double ljpVolts, double[] solvedCLs) = LjpCalculator.CalculateLjp(Ions, phis, TemperatureC);

        double[] differences = Enumerable.Range(0, Ions.Length)
                               .Select(i => solvedCLs[i] - originalCLs[i])
                               .ToArray();

        // Next we must normalize differences to a base value. Typically this is the absolute value of the original CL.
        // However, if original CL is extremely small or zero, normalizing to it will produce a huge or undefined error.

        // JLJP originally solved this by instead normalizing to the smallest non-zero original CL. I found this problematic
        // still because some original CLs may be extremely small, so calculated error remains very large.
        double smallestNonZeroCl = Ions.Select(x => x.CL).Where(CL => CL > 0).Min();

        // I prefer to normalize to a percentage of total CL
        double onePercentTotalCL = Ions.Select(x => x.CL).Sum() * .01;

        double[] divisors = Enumerable.Range(0, Ions.Length)
                            .Select(i => Math.Max(originalCLs[i], onePercentTotalCL))
                            .ToArray();

        double[] percentErrorCL = Enumerable.Range(0, EquationCount)
                                  .Select(i => 100 * differences[i] / divisors[i])
                                  .ToArray();

        return(new PhiEquationSolution(phis, solvedCLs, percentErrorCL, ljpVolts));
    }
コード例 #2
0
ファイル: IonSortingTests.cs プロジェクト: swharden/LJPcalc
    public void Test_BadOrder_FixedByAutoSort001()
    {
        /* Known to crash due to poor ion order https://github.com/swharden/LJPcalc/issues/9 */
        // fixed by autoSort prior to calculation

        var ionSet = new Ion[] {
            new Ion("Zn", 9, .0284),
            new Ion("K", 0, 3),
            new Ion("Cl", 18, 3.062),
            new Ion("Mg", 5, 3),
            new Ion("Ag", 1, 1),
        };

        ionSet = IonLibrary.Lookup(ionSet);

        // it does not solve in this order
        LjpResult result = LjpCalculator.SolvePhisThenCalculateLjp(ionSet, autoSort: false, maxIterations: 123);

        Assert.That(result.Iterations, Is.EqualTo(123));

        // but it does solve if allowed to auto-sort
        LjpResult result2 = LjpCalculator.SolvePhisThenCalculateLjp(ionSet, autoSort: true, maxIterations: 123);

        Assert.That(result2.LjpMillivolts, Is.EqualTo(-11.9).Within(0.5));
    }
コード例 #3
0
ファイル: IonSortingTests.cs プロジェクト: swharden/LJPcalc
    public void Test_BadOrder_FixedByAutoSort002()
    {
        /* Known to crash due to poor ion order https://github.com/swharden/LJPcalc/issues/9 */
        // fixed by autoSort prior to calculation

        var ionSet = new Ion[] {
            new Ion("K", 145, 2.8),
            new Ion("Na", 13, 145),
            new Ion("Cl", 10, 148.8),
            new Ion("Gluconate", 145, 0),
            new Ion("Mg", 1, 2),
            new Ion("Ca", 0, 1),
            new Ion("HEPES", 5, 5),
        };

        ionSet = IonLibrary.Lookup(ionSet);

        // it does not solve in this order
        LjpResult result = LjpCalculator.SolvePhisThenCalculateLjp(ionSet, autoSort: false, maxIterations: 123);

        Assert.That(result.Iterations, Is.EqualTo(123));

        // but it does solve if allowed to auto-sort
        LjpResult result2 = LjpCalculator.SolvePhisThenCalculateLjp(ionSet, autoSort: true, maxIterations: 123);

        Assert.That(result2.LjpMillivolts, Is.EqualTo(16.3).Within(0.5));
    }
コード例 #4
0
    public void Test_DifficultSet_Owen2013()
    {
        // This ion set came from https://www.nature.com/articles/nature12330

        var ionSet = new Ion[]
        {
            new Ion("K", 50, 3),
            new Ion("Gluconate", 50, 0),
            new Ion("Cs", 70, 0),
            new Ion("HSO3", 70, 0),
            new Ion("TEA (TetraethylAmmonium)", 10, 10),
            new Ion("Cl", 12, 131.6),
            new Ion("Mg", 5, 1.3),
            new Ion("HEPES", 10, 0),
            new Ion("EGTA(2-)", .3, 0),
            new Ion("Tris", 10, 0),
            new Ion("ATP (Adenosine 5'-Triphosphate)", 4, 0),
            new Ion("Na", 0.3, 139.25),
            new Ion("HCO3", 0, 26),
            new Ion("H2PO4", 0, 1.25),
            new Ion("Ca", 0, 2),
            new Ion("4-AP (4-aminopyridine)", 0, 5),
        };

        ionSet = IonLibrary.Lookup(ionSet);

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet, temperatureC: 33);

        Assert.That(ljp.LjpMillivolts, Is.EqualTo(15.1 - 3.3).Within(0.5));
    }
コード例 #5
0
    public void Test_LjpMathThrowsExceptionIf_MuIsZero()
    {
        var ionSet = new Ion[]
        {
            new Ion("A", charge: 1, conductivity: 7, c0: 3, cL: 3, phi: 6),
            new Ion("B", charge: 1, conductivity: 0, c0: 3, cL: 3, phi: 6),
            new Ion("C", charge: 1, conductivity: 7, c0: 3, cL: 3, phi: 6),
        };

        Assert.Throws <ArgumentException>(() => LjpCalculator.SolvePhisThenCalculateLjp(ionSet));
    }
コード例 #6
0
    public void Test_KnownIonSets_LjpWithinExpectedRange()
    {
        IKnownIonSet[] knowSets = Core.KnownIonSets.KnownSets.GetAll();
        Assert.That(knowSets, Is.Not.Null);
        Assert.That(knowSets, Is.Not.Empty);

        Parallel.ForEach(knowSets, (knownSet) =>
        {
            Ion[] ions       = IonLibrary.Lookup(knownSet.Ions);
            LjpResult result = LjpCalculator.SolvePhisThenCalculateLjp(ions, knownSet.Temperature_C);
            Assert.That(result.LjpMillivolts, Is.EqualTo(knownSet.Ljp_mV).Within(knownSet.Accuracy_mV), knownSet.Name);
        });
    }
コード例 #7
0
    public void Test_LjpMathIsAccurateWhen_ChargeAndConductanceAreSetManually()
    {
        // here the ions aren't looked up from a table but charge and mu are set manually
        var ionSet = new Ion[]
        {
            new Ion("Zn", 2, 52.8, 9, 0.0284),
            new Ion("K", 1, 73.5, 0, 3),
            new Ion("Cl", -1, 76.31, 18, 3.0568)
        };

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet, autoSort: false);

        Assert.That(ljp.LjpMillivolts, Is.EqualTo(-20.79558643).Within(1e-6));
    }
コード例 #8
0
    public void Test_LjpCalculationMatches_ExampleFromSourceCode()
    {
        /* From JLJP: https://github.com/swharden/JLJP/blob/2.0.0/src/Example.java */

        Ion Zn = new Ion("Zn", MolsPerCubicMeter(9), MolsPerCubicMeter(0.0284));
        Ion K  = new Ion("K", MolsPerCubicMeter(0), MolsPerCubicMeter(3));
        Ion Cl = new Ion("Cl", MolsPerCubicMeter(18), MolsPerCubicMeter(3.0568));

        var ionSet = new Ion[] { Zn, K, Cl };

        ionSet = IonLibrary.Lookup(ionSet);

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet, autoSort: false);

        Assert.That(ljp.LjpMillivolts, Is.EqualTo(-20.79558643).Within(1e-6));
    }
コード例 #9
0
    public void Test_LjpCalculationMatches_Harper004()
    {
        // ion set from Harper (1985) Table I
        // https://pubs.acs.org/doi/pdf/10.1021/j100255a022

        var ionSet = new Ion[]
        {
            new Ion("Zn", .4, .0186),
            new Ion("SO4", .4, .0186),
        };

        ionSet = IonLibrary.Lookup(ionSet);

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet);

        Assert.That(ljp.LjpMillivolts, Is.EqualTo(-8.1).Within(0.5));
    }
コード例 #10
0
    public void Test_LjpCalculationMatches_ExampleFromScreenshot()
    {
        /* Test came from screenshot on original JLJP website */

        var ionSet = new Ion[]
        {
            new Ion("Zn", 9, 0.0284),
            new Ion("K", 0, 3),
            new Ion("Cl", 18, 3.0568)
        };

        ionSet = IonLibrary.Lookup(ionSet);

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet, autoSort: false);

        Assert.That(ljp.LjpMillivolts, Is.EqualTo(-20.79558643).Within(1e-6));
    }
コード例 #11
0
    public void Test_LjpCalculationMatches_NgAndBarry001()
    {
        /* LJP for this test came from Ng and Barry (1994) Table 2 */

        // 50 mM NaCl : 50 mM KCl
        var ionSet = new Ion[]
        {
            new Ion("Na", 50, 0),
            new Ion("K", 0, 50),
            new Ion("Cl", 50, 50)
        };

        ionSet = IonLibrary.Lookup(ionSet);

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet);

        Assert.That(ljp.LjpMillivolts, Is.EqualTo(-4.3).Within(0.5));
    }
コード例 #12
0
    public void Test_LjpCalculationMatches_JPWin002()
    {
        // ion set shown in JPCalcWin manual (page 10)
        // https://tinyurl.com/wk7otn7

        var ionSet = new Ion[]
        {
            new Ion("Cs", 145, 0),
            new Ion("Na", 0, 145),
            new Ion("F", 125, 0),
            new Ion("Cl", 20, 145)
        };

        ionSet = IonLibrary.Lookup(ionSet);

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet);

        Assert.That(ljp.LjpMillivolts, Is.EqualTo(+8.71).Within(0.5));
    }
コード例 #13
0
    public void Test_LjpCalculationMatches_NgAndBarry005()
    {
        /* LJP for this test came from Ng and Barry (1994) Table 2 */

        // 100 CaCl2 : 100 MgCl2
        // Ca (100), Cl (200) : Mg (100) Cl (200)
        var ionSet = new Ion[]
        {
            new Ion("Ca", 100, 0),
            new Ion("Mg", 0, 100),
            new Ion("Cl", 200, 200)
        };

        ionSet = IonLibrary.Lookup(ionSet);

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet);

        Assert.That(ljp.LjpMillivolts, Is.EqualTo(+0.6).Within(0.5));
    }
コード例 #14
0
    public void Test_LjpCalculationMatches_NgAndBarry006()
    {
        /* LJP for this test came from Ng and Barry (1994) Table 2 */

        // 100 KCl + 2 CaCl2 : 100 LiCl + 2 CaCl2
        // K (100), Cl (104), Ca (2) : Li (100), Cl (104), Ca (2)
        var ionSet = new Ion[]
        {
            new Ion("Ca", 2, 2),
            new Ion("K", 100, 0),
            new Ion("Li", 0, 100),
            new Ion("Cl", 104, 104)
        };

        ionSet = IonLibrary.Lookup(ionSet);

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet);

        Assert.That(ljp.LjpMillivolts, Is.EqualTo(+6.4).Within(0.5));
    }
コード例 #15
0
ファイル: IonSortingTests.cs プロジェクト: swharden/LJPcalc
    public void Test_BadOrder_SecondFromLastSameConc()
    {
        // second to last ion cannot have same concentration on both sides
        var ionSet = new Ion[]
        {
            new Ion("Zn", 2, 2),
            new Ion("K", 3, 3),  // after sorting this is last (biggest absolute CL)
            new Ion("Mg", 2, 2),
            new Ion("Cl", 4, 0), // after sorting this is second to last (largest difference between C0 and CL)
        };

        ionSet = IonLibrary.Lookup(ionSet);

        Assert.Throws <InvalidOperationException>(() => LjpCalculator.SolvePhisThenCalculateLjp(ionSet, autoSort: false));

        LjpResult ljp = LjpCalculator.SolvePhisThenCalculateLjp(ionSet, autoSort: true);

        string ionOrder = string.Join(", ", ljp.Ions.Select(x => x.Name));

        Assert.That(ionOrder, Is.EqualTo("Zn, Mg, Cl, K"));
    }