'''Chemical Engineering Design Library (ChEDL). Utilities for process modeling.
Copyright (C) 2019, Caleb Bell <Caleb.Andrew.Bell@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''

import json
import pickle
from math import *

import numpy as np
import pytest
from chemicals.utils import rho_to_Vm, Vm_to_rho
from fluids.constants import *
from fluids.numerics import assert_close, assert_close1d, assert_close2d, assert_close3d, derivative, hessian, jacobian, normalize

from thermo import EXP_POLY_FIT
from thermo.bulk import *
from thermo.chemical_package import ChemicalConstantsPackage, PropertyCorrelationsPackage
from thermo.coolprop import PropsSI, has_CoolProp
from thermo.eos import *
from thermo.eos_mix import *
from thermo.equilibrium import *
from thermo.heat_capacity import *
from thermo.interface import *
from thermo.phase_change import *
from thermo.phases import *
from thermo.phases.phase_utils import fugacities_direct, lnphis_direct
from thermo.regular_solution import RegularSolution
from thermo.thermal_conductivity import *
from thermo.unifac import DOUFIP2016, DOUFSG, PSRKIP, PSRKSG, UFIP, UFSG, UNIFAC, VTPRIP, VTPRSG
from thermo.uniquac import UNIQUAC
from thermo.utils import LINEAR
from thermo.vapor_pressure import VaporPressure
from thermo.viscosity import *
from thermo.volume import *
from thermo.wilson import Wilson


def test_store_load_VirialCSP():
    Tcs = [190.564]
    Pcs = [4599000.0]
    omegas = [0.008]
    Vcs = [9.86e-05]
    model = VirialCSP(T=300.0, Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas, B_model=VIRIAL_B_PITZER_CURL, C_model=VIRIAL_C_ZERO)

    # Run the various checks for storing/loading
    model_copy = eval(repr(model))
    assert model_copy.B_pures() == model.B_pures()
    assert model_copy.C_pures() == model.C_pures()

    model_pickle = pickle.loads(pickle.dumps(model))
    assert model_pickle == model
    assert model_pickle.model_hash() == model.model_hash()
    assert VirialCSP.from_json(json.loads(json.dumps(model.as_json()))) == model

def test_store_load_VirialGas():
    Tcs = [190.564]
    Pcs = [4599000.0]
    omegas = [0.008]
    Vcs = [9.86e-05]
    HeatCapacityGases = [HeatCapacityGas(poly_fit=(50.0, 1000.0, [6.7703235945157e-22, -2.496905487234175e-18, 3.141019468969792e-15, -8.82689677472949e-13, -1.3709202525543862e-09, 1.232839237674241e-06, -0.0002832018460361874, 0.022944239587055416, 32.67333514157593]))]
    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas, B_model=VIRIAL_B_PITZER_CURL, C_model=VIRIAL_C_ZERO)
    zs = [1]
    PT = VirialGas(model, HeatCapacityGases=HeatCapacityGases, T=300.0, P=1e5, zs=zs)

    # Run the various checks for storing/loading
    phase_copy = eval(repr(PT))
    assert phase_copy.B() == PT.B()
    assert phase_copy.C() == PT.C()
    assert phase_copy == PT
    assert hash(phase_copy) == hash(PT)
    assert phase_copy.model_hash() == PT.model_hash()
    assert phase_copy.state_hash() == PT.state_hash()

    # Pickle checks
    phase_copy = pickle.loads(pickle.dumps(PT))
    assert phase_copy.B() == PT.B()
    assert phase_copy.C() == PT.C()
    assert phase_copy == PT
    assert hash(phase_copy) == hash(PT)
    assert phase_copy.model_hash() == PT.model_hash()
    assert phase_copy.state_hash() == PT.state_hash()

    # Simple json check
    phase_copy = VirialCSP.from_json(json.loads(json.dumps(PT.as_json())))
    assert phase_copy.B() == PT.B()
    assert phase_copy.C() == PT.C()
    assert phase_copy == PT
    assert hash(phase_copy) == hash(PT)
    assert phase_copy.model_hash() == PT.model_hash()
    assert phase_copy.state_hash() == PT.state_hash()


def test_virial_phase_pure_B_only_pitzer_curl():
    Tcs = [190.564]
    Pcs = [4599000.0]
    omegas = [0.008]
    Vcs = [9.86e-05]
    HeatCapacityGases = [HeatCapacityGas(poly_fit=(50.0, 1000.0, [6.7703235945157e-22, -2.496905487234175e-18, 3.141019468969792e-15, -8.82689677472949e-13, -1.3709202525543862e-09, 1.232839237674241e-06, -0.0002832018460361874, 0.022944239587055416, 32.67333514157593]))]
    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas, B_model=VIRIAL_B_PITZER_CURL, C_model=VIRIAL_C_ZERO)
    zs = [1]
    PT = VirialGas(model, HeatCapacityGases=HeatCapacityGases, T=300.0, P=1e5, zs=zs)

    assert_close(PT.rho(), 40.159125929164205, rtol=1e-13)
    assert_close(PT.V(), 0.02490094036817131, rtol=1e-13)
    assert_close(PT.B(), -4.2375251149270624e-05, rtol=1e-14)

    assert_close(PT.dB_dT(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).B(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.d2B_dT2(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).dB_dT(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.d3B_dT3(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).d2B_dT2(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.dC_dT(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).C(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.d2C_dT2(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).dC_dT(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.d3C_dT3(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).d2C_dT2(), PT.T, PT.T*3e-7), rtol=1e-7)


    assert_close(PT.to_TP_zs(T=PT.T, P=PT.P, zs=[1]).V(), PT.V())

    TV = PT.to(T=PT.T, V=PT.V(), zs=[1])
    assert_close(TV.P, PT.P, rtol=1e-13)

    PV = PT.to(P=PT.P, V=PT.V(), zs=[1])
    assert_close(PV.T, 300, rtol=1e-13)

    PT2 = PT.to(P=PT.P, T=PT.T, zs=[1])
    assert_close(PT2.V(), 0.02490094036817131, rtol=1e-13)
    assert_close(PT2.B(), -4.2375251149270624e-05, rtol=1e-14)

    assert_close(PT.dP_dT(), 334.86796331728914, rtol=1e-13)
    assert_close(PT.dP_dT(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).P, PT.T, PT.T*1e-7), rtol=1e-10)

    assert_close(PT.dP_dV(), -4009066.851663165, rtol=1e-13)
    assert_close(PT.dP_dV(), derivative(lambda V: PT.to(T=PT.T, V=V, zs=[1]).P, PT.V(), PT.V()*1e-7), rtol=1e-8)

    assert_close(PT.d2P_dTdV(), -13486.814969771252, rtol=1e-13)
    assert_close(PT.d2P_dTdV(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).dP_dV(), PT.T, PT.T*1e-7), rtol=1e-9)

    assert_close(PT.d2P_dV2(), 321451403.1386218, rtol=1e-13)
    assert_close(PT.d2P_dV2(), derivative(lambda V: PT.to(T=PT.T, V=V, zs=[1]).dP_dV(), PT.V(), PT.V()*1e-7), rtol=1e-8)

    assert_close(PT.d2P_dT2(), -0.0020770847374848075, rtol=1e-13)
    assert_close(PT.d2P_dT2(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).dP_dT(), PT.T, PT.T*8e-7), rtol=1e-6)

    assert_close(PT.H_dep(), 15.708867544147836, rtol=1e-13)
    H_dep_Poling = (-(PT.B() - PT.T*PT.dB_dT())/PT.V())*R*PT.T
    assert_close(PT.H_dep(), H_dep_Poling, rtol=1e-13)

    assert_close(PT.dH_dep_dT(), -0.03958097657787867, rtol=1e-13)
    assert_close(PT.dH_dep_dT(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).H_dep(), PT.T, PT.T*3e-7), rtol=1e-7)

    S_dep_Poling = -((PT.B() + PT.T*PT.dB_dT())/PT.V()- log(PT.Z()))*R
    assert_close(PT.S_dep(), -0.03822578258348812, rtol=1e-13)
    assert_close(PT.S_dep(), S_dep_Poling, rtol=1e-13)

    assert_close(PT.dS_dep_dT(), 0.0001793175995307205, rtol=1e-13)
    assert_close(PT.dS_dep_dT(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).S_dep(), PT.T, PT.T*3e-7), rtol=1e-7)

def test_virial_phase_ternary_B_only_pitzer_curl():
    CASs = ['7727-37-9', '74-82-8', '124-38-9']
    atomss = [{'N': 2}, {'C': 1, 'H': 4}, {'C': 1, 'O': 2}]
    Tcs = [126.2, 190.564, 304.2]
    Tcs_np = np.array(Tcs)
    Pcs = [3394387.5, 4599000.0, 7376460.0]
    Pcs_np = np.array(Pcs)
    Vcs = [8.95e-05, 9.86e-05, 9.4e-05]
    Vcs_np = np.array(Vcs)
    omegas = [0.04, 0.008, 0.2252]
    omegas_np = np.array(omegas)
    N = 3

    HeatCapacityGases = [HeatCapacityGas(CASRN="7727-37-9", MW=28.0134, similarity_variable=0.07139440410660612, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-6.496329615255804e-23, 2.1505678500404716e-19, -2.2204849352453665e-16, 1.7454757436517406e-14, 9.796496485269412e-11, -4.7671178529502835e-08, 8.384926355629239e-06, -0.0005955479316119903, 29.114778709934264])),
     HeatCapacityGas(CASRN="74-82-8", MW=16.04246, similarity_variable=0.3116728980468083, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [6.7703235945157e-22, -2.496905487234175e-18, 3.141019468969792e-15, -8.82689677472949e-13, -1.3709202525543862e-09, 1.232839237674241e-06, -0.0002832018460361874, 0.022944239587055416, 32.67333514157593])),
     HeatCapacityGas(CASRN="124-38-9", MW=44.0095, similarity_variable=0.0681671002851657, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-3.1115474168865828e-21, 1.39156078498805e-17, -2.5430881416264243e-14, 2.4175307893014295e-11, -1.2437314771044867e-08, 3.1251954264658904e-06, -0.00021220221928610925, 0.000884685506352987, 29.266811602924644]))]

    # Specifically test without kijs
    kijs = [[0.0]*3 for _ in range(N)]
    kijs_np = np.array(kijs)

    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas,
                                  B_model=VIRIAL_B_ABBOTT,
                                  cross_B_model=VIRIAL_B_ABBOTT,
                                  cross_B_model_kijs=kijs,
                                  C_model=VIRIAL_C_ZERO)
    model_np = VirialCSP(Tcs=Tcs_np, Pcs=Pcs_np, Vcs=Vcs_np, omegas=omegas_np,
                              B_model=VIRIAL_B_ABBOTT,
                              cross_B_model=VIRIAL_B_ABBOTT,
                              cross_B_model_kijs=kijs_np,
                              C_model=VIRIAL_C_ZERO)


    T = 300
    P = 1e5
    zs = [.02, .92, .06]
    zs_np = np.array(zs)
    gas = VirialGas(model=model, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='theory',
                T=T, P=P, zs=zs)
    gas_np = VirialGas(model=model_np, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='theory',
                T=T, P=P, zs=zs_np)


    # Test some basics
    B, dB, d2B, d3B =  -4.413104126753512e-05, 3.9664296537847194e-07,  -3.488354169403768e-09,  4.274046794787785e-11
    assert_close(gas.B(), B, rtol=1e-13)
    assert_close(gas.dB_dT(), dB, rtol=1e-13)
    assert_close(gas.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas.d3B_dT3(), d3B, rtol=1e-13)
    assert_close(gas_np.B(), B, rtol=1e-13)
    assert_close(gas_np.dB_dT(), dB, rtol=1e-13)
    assert_close(gas_np.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas_np.d3B_dT3(), d3B, rtol=1e-13)


    for val in (gas.B(), gas.dB_dT(), gas.d2B_dT2(), gas.d3B_dT3(),
                gas_np.B(), gas_np.dB_dT(), gas_np.d2B_dT2(), gas_np.d3B_dT3()):
        assert type(val) is float


    for val in (gas.C(), gas.dC_dT(), gas.d2C_dT2(), gas.d3C_dT3(),
                gas_np.C(), gas_np.dC_dT(), gas_np.d2C_dT2(), gas_np.d3C_dT3()):
        assert val == 0.0
        assert type(val) is float

    V = 0.024899178456922296
    assert_close(gas.V(), V, rtol=1e-13)
    assert_close(gas_np.V(), V, rtol=1e-13)


    # Check the B and T derivatives
    assert_close(gas.dB_dT(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).B(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d2B_dT2(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).dB_dT(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d3B_dT3(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).d2B_dT2(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.dC_dT(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).C(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d2C_dT2(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).dC_dT(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d3C_dT3(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).d2C_dT2(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas_np.dB_dT(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).B(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d2B_dT2(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).dB_dT(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d3B_dT3(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).d2B_dT2(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.dC_dT(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).C(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d2C_dT2(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).dC_dT(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d3C_dT3(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).d2C_dT2(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)


    # Mole fraction derivatives of B, including its temperature derivatives
    dB_dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).B(), zs, perturbation=1e-7)
    dB_dzs_expect = [-4.1226874244236366e-05, -8.550397384968317e-05, -0.0001462314851412832]
    assert_close1d(gas.dB_dzs(), dB_dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.dB_dzs(), dB_dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.dB_dzs(), np.ndarray)
    assert_close1d(dB_dzs, gas.dB_dzs())

    d2B_dTdzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).dB_dT(), zs, perturbation=1e-7)
    d2B_dTdzs_expect = [5.260937128431123e-07, 7.722240212573212e-07, 1.2052992823891033e-06]
    assert_close1d(gas.d2B_dTdzs(), d2B_dTdzs_expect, rtol=1e-13)
    assert_close1d(gas_np.d2B_dTdzs(), d2B_dTdzs_expect, rtol=1e-13)
    assert isinstance(gas_np.d2B_dTdzs(), np.ndarray)
    assert_close1d(d2B_dTdzs, gas.d2B_dTdzs())

    d3B_dT2dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).d2B_dT2(), zs, perturbation=1e-7)
    d3B_dT2dzs_expect = [-4.596636740922754e-09, -6.7485932495824905e-09, -1.1267830239553159e-08]
    assert_close1d(gas.d3B_dT2dzs(), d3B_dT2dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.d3B_dT2dzs(), d3B_dT2dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.d3B_dT2dzs(), np.ndarray)
    assert_close1d(d3B_dT2dzs, gas.d3B_dT2dzs())

    d4B_dT3dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).d3B_dT3(), zs, perturbation=1e-7)
    d4B_dT3dzs_expect = [5.580371109330211e-11, 8.195352679443102e-11, 1.4946028371688516e-10]
    assert_close1d(gas.d4B_dT3dzs(), d4B_dT3dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.d4B_dT3dzs(), d4B_dT3dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.d4B_dT3dzs(), np.ndarray)
    assert_close1d(d4B_dT3dzs, gas.d4B_dT3dzs())


    # Second mole fraction derivatives
    d2B_dzizjs = hessian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).B(), zs, perturbation=125e-5)
    d2B_dzizjs_expect = [[-1.0639357784985337e-05, -3.966321845899801e-05, -7.53987684376414e-05], [-3.966321845899801e-05, -8.286257232134107e-05, -0.00014128571574782375], [-7.53987684376414e-05, -0.00014128571574782375, -0.00024567752140887547]]
    assert_close2d(d2B_dzizjs_expect, gas.d2B_dzizjs(), rtol=1e-13)
    assert_close1d(gas_np.d2B_dzizjs(), d2B_dzizjs_expect, rtol=1e-13)
    assert_close2d(d2B_dzizjs, gas.d2B_dzizjs(), rtol=1e-6)
    assert isinstance(gas_np.d2B_dzizjs(), np.ndarray)

    d3B_dTdzizjs = hessian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).dB_dT(), zs, perturbation=125e-5)
    d3B_dTdzizjs_expect = [[3.4972455964751027e-07, 5.136445106798038e-07, 7.757711970790444e-07], [5.136445106798038e-07, 7.522549586697655e-07, 1.1646094844590136e-06], [7.757711970790444e-07, 1.1646094844590136e-06, 1.9723855457538296e-06]]
    assert_close2d(d3B_dTdzizjs_expect, gas.d3B_dTdzizjs(), rtol=1e-13)
    assert_close1d(gas_np.d3B_dTdzizjs(), d3B_dTdzizjs_expect, rtol=1e-13)
    assert_close2d(d3B_dTdzizjs, gas.d3B_dTdzizjs(), rtol=1e-6)
    assert isinstance(gas_np.d3B_dTdzizjs(), np.ndarray)

    d4B_dT2dzizjs = hessian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).d2B_dT2(), zs, perturbation=125e-5)
    d4B_dT2dzizjs_expect = [[-3.0445377873470256e-09, -4.4720495115515925e-09, -7.024340575805803e-09], [-4.4720495115515925e-09, -6.536647166125688e-09, -1.0757281108597095e-08], [-7.024340575805803e-09, -1.0757281108597095e-08, -2.051074680212858e-08]]
    assert_close2d(d4B_dT2dzizjs_expect, gas.d4B_dT2dzizjs(), rtol=1e-13)
    assert_close1d(gas_np.d4B_dT2dzizjs(), d4B_dT2dzizjs_expect, rtol=1e-13)
    assert_close2d(d4B_dT2dzizjs, gas.d4B_dT2dzizjs(), rtol=1e-6)
    assert isinstance(gas_np.d4B_dT2dzizjs(), np.ndarray)

    # Third mole fraction derivatives
    d3B_dzizjzks_expect = [[[0.0]*N for _ in range(N)] for _ in range(N)]
    assert_close3d(d3B_dzizjzks_expect, gas.d3B_dzizjzks(), atol=0)
    assert_close3d(d3B_dzizjzks_expect, gas.d4B_dTdzizjzks(), atol=0)
    assert_close3d(d3B_dzizjzks_expect, gas.d5B_dT2dzizjzks(), atol=0)
    assert_close3d(d3B_dzizjzks_expect, gas.d6B_dT3dzizjzks(), atol=0)

    assert_close3d(d3B_dzizjzks_expect, gas_np.d3B_dzizjzks(), atol=0)
    assert isinstance(gas_np.d3B_dzizjzks(), np.ndarray)
    assert_close3d(d3B_dzizjzks_expect, gas_np.d4B_dTdzizjzks(), atol=0)
    assert isinstance(gas_np.d4B_dTdzizjzks(), np.ndarray)
    assert_close3d(d3B_dzizjzks_expect, gas_np.d5B_dT2dzizjzks(), atol=0)
    assert isinstance(gas_np.d5B_dT2dzizjzks(), np.ndarray)
    assert_close3d(d3B_dzizjzks_expect, gas_np.d6B_dT3dzizjzks(), atol=0)
    assert isinstance(gas_np.d6B_dT3dzizjzks(), np.ndarray)


    # Mole number derivatives
    def dB_dns_to_jac(ns):
        zs = normalize(ns)
        return gas.to(T=T, P=P, zs=zs).B()

    dB_dns = jacobian(dB_dns_to_jac, zs, perturbation=1e-7)
    dB_dns_expect = [4.7035208290833874e-05, 2.7581086853870686e-06, -5.796940260621295e-05]
    assert_close1d(gas.dB_dns(), dB_dns_expect, rtol=1e-13)
    assert_close1d(gas_np.dB_dns(), dB_dns_expect, rtol=1e-13)
    assert isinstance(gas_np.dB_dns(), np.ndarray)
    assert_close1d(dB_dns, gas.dB_dns())

    # Partial derivatives
    dnB_dns_expect = [2.904167023298754e-06, -4.137293258214805e-05, -0.00010210044387374808]
    assert_close1d(gas.dnB_dns(), dnB_dns_expect, rtol=1e-13)
    assert_close1d(gas_np.dnB_dns(), dnB_dns_expect, rtol=1e-13)
    assert isinstance(gas_np.dnB_dns(), np.ndarray)

def test_virial_phase_ternary_B_only_pitzer_curl_no_interactions():
    CASs = ['7727-37-9', '74-82-8', '124-38-9']
    atomss = [{'N': 2}, {'C': 1, 'H': 4}, {'C': 1, 'O': 2}]
    Tcs = [126.2, 190.564, 304.2]
    Tcs_np = np.array(Tcs)
    Pcs = [3394387.5, 4599000.0, 7376460.0]
    Pcs_np = np.array(Pcs)
    Vcs = [8.95e-05, 9.86e-05, 9.4e-05]
    Vcs_np = np.array(Vcs)
    omegas = [0.04, 0.008, 0.2252]
    omegas_np = np.array(omegas)
    N = 3

    HeatCapacityGases = [HeatCapacityGas(CASRN="7727-37-9", MW=28.0134, similarity_variable=0.07139440410660612, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-6.496329615255804e-23, 2.1505678500404716e-19, -2.2204849352453665e-16, 1.7454757436517406e-14, 9.796496485269412e-11, -4.7671178529502835e-08, 8.384926355629239e-06, -0.0005955479316119903, 29.114778709934264])),
     HeatCapacityGas(CASRN="74-82-8", MW=16.04246, similarity_variable=0.3116728980468083, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [6.7703235945157e-22, -2.496905487234175e-18, 3.141019468969792e-15, -8.82689677472949e-13, -1.3709202525543862e-09, 1.232839237674241e-06, -0.0002832018460361874, 0.022944239587055416, 32.67333514157593])),
     HeatCapacityGas(CASRN="124-38-9", MW=44.0095, similarity_variable=0.0681671002851657, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-3.1115474168865828e-21, 1.39156078498805e-17, -2.5430881416264243e-14, 2.4175307893014295e-11, -1.2437314771044867e-08, 3.1251954264658904e-06, -0.00021220221928610925, 0.000884685506352987, 29.266811602924644]))]

    # Specifically test without kijs
    kijs = [[0.0]*3 for _ in range(N)]
    kijs_np = np.array(kijs)

    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas,
                                  B_model=VIRIAL_B_ABBOTT,
                                  cross_B_model=VIRIAL_B_ABBOTT,
                                  cross_B_model_kijs=kijs,
                                  C_model=VIRIAL_C_ZERO)
    model_np = VirialCSP(Tcs=Tcs_np, Pcs=Pcs_np, Vcs=Vcs_np, omegas=omegas_np,
                              B_model=VIRIAL_B_ABBOTT,
                              cross_B_model=VIRIAL_B_ABBOTT,
                              cross_B_model_kijs=kijs_np,
                              C_model=VIRIAL_C_ZERO)


    T = 300
    P = 1e5
    zs = [.02, .92, .06]
    zs_np = np.array(zs)
    gas = VirialGas(model=model, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='linear',
                T=T, P=P, zs=zs)
    gas_np = VirialGas(model=model_np, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='linear',
                T=T, P=P, zs=zs_np)


    # Test some basics
    B, dB, d2B, d3B = (-4.559350248793301e-05, 4.0870609295718214e-07, -3.6526254783551437e-09, 4.574694317862753e-11)
    assert_close(gas.B(), B, rtol=1e-13)
    assert_close(gas.dB_dT(), dB, rtol=1e-13)
    assert_close(gas.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas.d3B_dT3(), d3B, rtol=1e-13)
    assert_close(gas_np.B(), B, rtol=1e-13)
    assert_close(gas_np.dB_dT(), dB, rtol=1e-13)
    assert_close(gas_np.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas_np.d3B_dT3(), d3B, rtol=1e-13)


    for val in (gas.B(), gas.dB_dT(), gas.d2B_dT2(), gas.d3B_dT3(),
                gas_np.B(), gas_np.dB_dT(), gas_np.d2B_dT2(), gas_np.d3B_dT3()):
        assert type(val) is float


    for val in (gas.C(), gas.dC_dT(), gas.d2C_dT2(), gas.d3C_dT3(),
                gas_np.C(), gas_np.dC_dT(), gas_np.d2C_dT2(), gas_np.d3C_dT3()):
        assert val == 0.0
        assert type(val) is float

    V = 0.024897710706483875
    assert_close(gas.V(), V, rtol=1e-13)
    assert_close(gas_np.V(), V, rtol=1e-13)


    # Check the B and T derivatives
    assert_close(gas.dB_dT(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).B(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d2B_dT2(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).dB_dT(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d3B_dT3(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).d2B_dT2(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.dC_dT(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).C(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d2C_dT2(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).dC_dT(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d3C_dT3(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).d2C_dT2(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas_np.dB_dT(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).B(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d2B_dT2(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).dB_dT(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d3B_dT3(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).d2B_dT2(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.dC_dT(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).C(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d2C_dT2(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).dC_dT(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d3C_dT3(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).d2C_dT2(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)


    # Mole fraction derivatives of B, including its temperature derivatives
    dB_dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).B(), zs, perturbation=1e-7)
    dB_dzs_expect =  [-5.3196788924926625e-06, -4.143128616067054e-05, -0.00012283876070443773]
    assert_close1d(gas.dB_dzs(), dB_dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.dB_dzs(), dB_dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.dB_dzs(), np.ndarray)
    assert_close1d(dB_dzs, gas.dB_dzs())

    d2B_dTdzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).dB_dT(), zs, perturbation=1e-7)
    d2B_dTdzs_expect =  [1.7486227982375503e-07, 3.761274793348827e-07, 9.861927728769148e-07]
    assert_close1d(gas.d2B_dTdzs(), d2B_dTdzs_expect, rtol=1e-13)
    assert_close1d(gas_np.d2B_dTdzs(), d2B_dTdzs_expect, rtol=1e-13)
    assert isinstance(gas_np.d2B_dTdzs(), np.ndarray)
    assert_close1d(d2B_dTdzs, gas.d2B_dTdzs())

    d3B_dT2dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).d2B_dT2(), zs, perturbation=4e-7)
    d3B_dT2dzs_expect = [-1.5222688936735118e-09, -3.268323583062844e-09, -1.025537340106429e-08]
    assert_close1d(gas.d3B_dT2dzs(), d3B_dT2dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.d3B_dT2dzs(), d3B_dT2dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.d3B_dT2dzs(), np.ndarray)
    assert_close1d(d3B_dT2dzs, gas.d3B_dT2dzs())

    d4B_dT3dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).d3B_dT3(), zs, perturbation=1e-7)
    d4B_dT3dzs_expect = [1.8385020623121148e-11, 3.9368119318647694e-11, 1.5267621655015376e-10]
    assert_close1d(gas.d4B_dT3dzs(), d4B_dT3dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.d4B_dT3dzs(), d4B_dT3dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.d4B_dT3dzs(), np.ndarray)
    assert_close1d(d4B_dT3dzs, gas.d4B_dT3dzs())


    # Second mole fraction derivatives
    d2B_dzizjs = hessian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).B(), zs, perturbation=125e-5)
    d2B_dzizjs_expect = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
    assert_close2d(d2B_dzizjs_expect, gas.d2B_dzizjs(), rtol=1e-13)
    assert_close1d(gas_np.d2B_dzizjs(), d2B_dzizjs_expect, rtol=1e-13)
    assert_close2d(d2B_dzizjs, gas.d2B_dzizjs(), rtol=1e-6)
    assert isinstance(gas_np.d2B_dzizjs(), np.ndarray)

    d3B_dTdzizjs = hessian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).dB_dT(), zs, perturbation=125e-5)
    d3B_dTdzizjs_expect =  [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
    assert_close2d(d3B_dTdzizjs_expect, gas.d3B_dTdzizjs(), rtol=1e-13)
    assert_close1d(gas_np.d3B_dTdzizjs(), d3B_dTdzizjs_expect, rtol=1e-13)
    assert_close2d(d3B_dTdzizjs, gas.d3B_dTdzizjs(), atol=1e-6)
    assert isinstance(gas_np.d3B_dTdzizjs(), np.ndarray)

    d4B_dT2dzizjs = hessian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).d2B_dT2(), zs, perturbation=125e-5)
    d4B_dT2dzizjs_expect =  [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
    assert_close2d(d4B_dT2dzizjs_expect, gas.d4B_dT2dzizjs(), rtol=1e-13)
    assert_close1d(gas_np.d4B_dT2dzizjs(), d4B_dT2dzizjs_expect, rtol=1e-13)
    assert_close2d(d4B_dT2dzizjs, gas.d4B_dT2dzizjs(), atol=1e-6)
    assert isinstance(gas_np.d4B_dT2dzizjs(), np.ndarray)

    # Third mole fraction derivatives
    d3B_dzizjzks_expect = [[[0.0]*N for _ in range(N)] for _ in range(N)]
    assert_close3d(d3B_dzizjzks_expect, gas.d3B_dzizjzks(), atol=0)
    assert_close3d(d3B_dzizjzks_expect, gas.d4B_dTdzizjzks(), atol=0)
    assert_close3d(d3B_dzizjzks_expect, gas.d5B_dT2dzizjzks(), atol=0)
    assert_close3d(d3B_dzizjzks_expect, gas.d6B_dT3dzizjzks(), atol=0)

    assert_close3d(d3B_dzizjzks_expect, gas_np.d3B_dzizjzks(), atol=0)
    assert isinstance(gas_np.d3B_dzizjzks(), np.ndarray)
    assert_close3d(d3B_dzizjzks_expect, gas_np.d4B_dTdzizjzks(), atol=0)
    assert isinstance(gas_np.d4B_dTdzizjzks(), np.ndarray)
    assert_close3d(d3B_dzizjzks_expect, gas_np.d5B_dT2dzizjzks(), atol=0)
    assert isinstance(gas_np.d5B_dT2dzizjzks(), np.ndarray)
    assert_close3d(d3B_dzizjzks_expect, gas_np.d6B_dT3dzizjzks(), atol=0)
    assert isinstance(gas_np.d6B_dT3dzizjzks(), np.ndarray)


    # Mole number derivatives
    def dB_dns_to_jac(ns):
        zs = normalize(ns)
        return gas.to(T=T, P=P, zs=zs).B()

    dB_dns = jacobian(dB_dns_to_jac, zs, perturbation=.5e-7)
    dB_dns_expect = [4.0273823595440354e-05, 4.162216327262471e-06, -7.724525821650472e-05]
    assert_close1d(gas.dB_dns(), dB_dns_expect, rtol=1e-13)
    assert_close1d(gas_np.dB_dns(), dB_dns_expect, rtol=1e-13)
    assert isinstance(gas_np.dB_dns(), np.ndarray)
    assert_close1d(dB_dns, gas.dB_dns())

    # Partial derivatives
    dnB_dns_expect = [-5.3196788924926625e-06, -4.143128616067054e-05, -0.00012283876070443773]
    assert_close1d(gas.dnB_dns(), dnB_dns_expect, rtol=1e-13)
    assert_close1d(gas_np.dnB_dns(), dnB_dns_expect, rtol=1e-13)
    assert isinstance(gas_np.dnB_dns(), np.ndarray)


def test_virial_phase_pure_BC_pitzer_curl_orbey_vera():
    Tcs = [190.564]
    Pcs = [4599000.0]
    omegas = [0.008]
    Vcs = [9.86e-05]
    HeatCapacityGases = [HeatCapacityGas(poly_fit=(50.0, 1000.0, [6.7703235945157e-22, -2.496905487234175e-18, 3.141019468969792e-15, -8.82689677472949e-13, -1.3709202525543862e-09, 1.232839237674241e-06, -0.0002832018460361874, 0.022944239587055416, 32.67333514157593]))]
    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas, B_model=VIRIAL_B_PITZER_CURL, C_model=VIRIAL_C_ORBEY_VERA)
    zs = [1]
    gas = PT = VirialGas(model, HeatCapacityGases=HeatCapacityGases, T=300.0, P=1e5, zs=zs, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')

    assert_close(PT.rho(), 40.15896568252847, rtol=1e-13)
    assert_close(PT.V(), 0.02490103973058896, rtol=1e-13)
    assert_close(PT.B(), -4.2375251149270624e-05, rtol=1e-14)
    assert_close(PT.C(), 2.4658163172184193e-09, rtol=1e-14)

    assert_close(PT.dB_dT(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).B(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.d2B_dT2(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).dB_dT(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.d3B_dT3(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).d2B_dT2(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.dC_dT(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).C(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.d2C_dT2(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).dC_dT(), PT.T, PT.T*3e-7), rtol=1e-7)
    assert_close(PT.d3C_dT3(), derivative(lambda T: PT.to(T=T, P=PT.P, zs=zs).d2C_dT2(), PT.T, PT.T*3e-7), rtol=1e-7)


    assert_close(PT.to_TP_zs(T=PT.T, P=PT.P, zs=[1]).V(), PT.V())

    TV = PT.to(T=PT.T, V=PT.V(), zs=[1])
    assert_close(TV.P, PT.P, rtol=1e-13)
    assert_close(TV.B(), PT.B(), rtol=1e-13)
    assert_close(TV.C(), PT.C(), rtol=1e-13)

    PV = PT.to(P=PT.P, V=PT.V(), zs=[1])
    assert_close(PV.T, 300, rtol=1e-13)
    assert_close(PV.B(), PT.B(), rtol=1e-13)
    assert_close(PV.C(), PT.C(), rtol=1e-13)

    PT2 = PT.to(P=PT.P, T=PT.T, zs=[1])
    assert_close(PT2.B(), PT.B(), rtol=1e-13)
    assert_close(PT2.C(), PT.C(), rtol=1e-13)
    assert_close(PT2.V(), 0.02490103973058896, rtol=1e-13)
    assert_close(PT2.B(), -4.2375251149270624e-05, rtol=1e-14)
    assert_close(PT2.C(),  2.4658163172184193e-09, rtol=1e-14)

    assert_close(PT.dP_dT(), 334.8667253234988, rtol=1e-13)
    assert_close(PT.dP_dT(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).P, PT.T, PT.T*1e-7), rtol=1e-7)

    assert_close(PT.dP_dV(), -4009082.903515464, rtol=1e-13)
    assert_close(PT.dP_dV(), derivative(lambda V: PT.to(T=PT.T, V=V, zs=[1]).P, PT.V(), PT.V()*1e-7), rtol=1e-8)

    assert_close(PT.d2P_dTdV(), -13486.719326292223, rtol=1e-13)
    assert_close(PT.d2P_dTdV(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).dP_dV(), PT.T, PT.T*1e-7), rtol=1e-7)

    assert_close(PT.d2P_dV2(), 321455270.893362, rtol=1e-13)
    assert_close(PT.d2P_dV2(), derivative(lambda V: PT.to(T=PT.T, V=V, zs=[1]).dP_dV(), PT.V(), PT.V()*1e-7), rtol=1e-8)

    assert_close(PT.d2P_dT2(), -0.0020701719078863546, rtol=1e-13)
    assert_close(PT.d2P_dT2(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).dP_dT(), PT.T, PT.T*8e-7), rtol=1e-6)

    assert_close(PT.H_dep(), 15.694307202591004, rtol=1e-13)

    assert_close(PT.dH_dep_dT(), -0.03955760203920011, rtol=1e-13)
    assert_close(PT.dH_dep_dT(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).H_dep(), PT.T, PT.T*3e-7), rtol=1e-7)

    assert_close(PT.S_dep(), -0.03819378035089749, rtol=1e-13)

    assert_close(PT.dS_dep_dT(), 0.00017912859867798742, rtol=1e-13)
    assert_close(PT.dS_dep_dT(), derivative(lambda T: PT.to(T=T, V=PT.V(), zs=[1]).S_dep(), PT.T, PT.T*3e-7), rtol=1e-7)


def test_virial_phase_ternary_BC_pitzer_curl_orbey_vera():
    CASs = ['7727-37-9', '74-82-8', '124-38-9']
    atomss = [{'N': 2}, {'C': 1, 'H': 4}, {'C': 1, 'O': 2}]
    Tcs = [126.2, 190.564, 304.2]
    Tcs_np = np.array(Tcs)
    Pcs = [3394387.5, 4599000.0, 7376460.0]
    Pcs_np = np.array(Pcs)
    Vcs = [8.95e-05, 9.86e-05, 9.4e-05]
    Vcs_np = np.array(Vcs)
    omegas = [0.04, 0.008, 0.2252]
    omegas_np = np.array(omegas)
    N = 3

    HeatCapacityGases = [HeatCapacityGas(CASRN="7727-37-9", MW=28.0134, similarity_variable=0.07139440410660612, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-6.496329615255804e-23, 2.1505678500404716e-19, -2.2204849352453665e-16, 1.7454757436517406e-14, 9.796496485269412e-11, -4.7671178529502835e-08, 8.384926355629239e-06, -0.0005955479316119903, 29.114778709934264])),
     HeatCapacityGas(CASRN="74-82-8", MW=16.04246, similarity_variable=0.3116728980468083, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [6.7703235945157e-22, -2.496905487234175e-18, 3.141019468969792e-15, -8.82689677472949e-13, -1.3709202525543862e-09, 1.232839237674241e-06, -0.0002832018460361874, 0.022944239587055416, 32.67333514157593])),
     HeatCapacityGas(CASRN="124-38-9", MW=44.0095, similarity_variable=0.0681671002851657, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-3.1115474168865828e-21, 1.39156078498805e-17, -2.5430881416264243e-14, 2.4175307893014295e-11, -1.2437314771044867e-08, 3.1251954264658904e-06, -0.00021220221928610925, 0.000884685506352987, 29.266811602924644]))]

    # Specifically test without kijs
    kijs = [[0.0]*3 for _ in range(N)]
    kijs_np = np.array(kijs)

    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas,
                                  B_model=VIRIAL_B_ABBOTT,
                                  cross_B_model=VIRIAL_B_ABBOTT,
                                  cross_B_model_kijs=kijs,
                                  C_model=VIRIAL_C_ORBEY_VERA)
    model_np = VirialCSP(Tcs=Tcs_np, Pcs=Pcs_np, Vcs=Vcs_np, omegas=omegas_np,
                              B_model=VIRIAL_B_ABBOTT,
                              cross_B_model=VIRIAL_B_ABBOTT,
                              cross_B_model_kijs=kijs_np,
                              C_model=VIRIAL_C_ORBEY_VERA)


    T = 300
    P = 1e5
    zs = [.02, .92, .06]
    zs_np = np.array(zs)
    gas = VirialGas(model=model, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz',
                T=T, P=P, zs=zs)
    gas_np = VirialGas(model=model_np, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz',
                T=T, P=P, zs=zs_np)

    with pytest.raises(ValueError):
        gas.to(T=T, zs=zs)
    with pytest.raises(ValueError):
        gas.to(P=P, zs=zs)
    with pytest.raises(ValueError):
        gas.to(V=gas.V(), zs=zs)

    # Test some basics
    B, dB, d2B, d3B =  -4.413104126753512e-05, 3.9664296537847194e-07,  -3.488354169403768e-09,  4.274046794787785e-11
    assert_close(gas.B(), B, rtol=1e-13)
    assert_close(gas.dB_dT(), dB, rtol=1e-13)
    assert_close(gas.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas.d3B_dT3(), d3B, rtol=1e-13)
    assert_close(gas_np.B(), B, rtol=1e-13)
    assert_close(gas_np.dB_dT(), dB, rtol=1e-13)
    assert_close(gas_np.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas_np.d3B_dT3(), d3B, rtol=1e-13)


    for val in (gas.B(), gas.dB_dT(), gas.d2B_dT2(), gas.d3B_dT3(),
                gas_np.B(), gas_np.dB_dT(), gas_np.d2B_dT2(), gas_np.d3B_dT3()):
        assert type(val) is float


    for val in (gas.C(), gas.dC_dT(), gas.d2C_dT2(), gas.d3C_dT3(),
                gas_np.C(), gas_np.dC_dT(), gas_np.d2C_dT2(), gas_np.d3C_dT3()):
        assert type(val) is float

    V = 0.0248992803360438
    assert_close(gas.V(), V, rtol=1e-13)
    assert_close(gas_np.V(), V, rtol=1e-13)


    # Check the B and T derivatives
    assert_close(gas.dB_dT(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).B(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d2B_dT2(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).dB_dT(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d3B_dT3(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).d2B_dT2(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.dC_dT(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).C(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d2C_dT2(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).dC_dT(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d3C_dT3(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).d2C_dT2(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas_np.dB_dT(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).B(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d2B_dT2(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).dB_dT(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d3B_dT3(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).d2B_dT2(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.dC_dT(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).C(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d2C_dT2(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).dC_dT(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d3C_dT3(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).d2C_dT2(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)

    B_mat = gas.model.B_interactions()
    assert_close1d(gas.model.B_pures(), [B_mat[i][i] for i in range(N)], rtol=1e-13)

    assert_close(gas.d2T_dV2(), 6030.193454056163, rtol=1e-11)
    assert_close(gas.d2T_dV2_P(), 6030.193454056163, rtol=1e-11)
    assert_close(gas.d2V_dT2(), -3.5160077601372554e-09, rtol=1e-11)

    # Mole fraction derivatives of B, including its temperature derivatives
    dC_dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).C(), zs, perturbation=1e-7)
    dC_dzs_expect = [6.196410187720199e-09, 7.51935295282256e-09, 9.02453803131555e-09]
    assert_close1d(gas.dC_dzs(), dC_dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.dC_dzs(), dC_dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.dC_dzs(), np.ndarray)
    assert_close1d(dC_dzs, gas.dC_dzs())


    # Second mole fraction derivatives
    d2C_dzizjs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).dC_dzs(), zs, scalar=False, perturbation=1e-7)
    d2C_dzizjs_expect = [[1.0334498744432369e-08, 1.232601605536606e-08, 1.4103260493582947e-08], [1.232601605536606e-08, 1.4914908015449092e-08, 1.7841170172077197e-08], [1.4103260493582947e-08, 1.7841170172077197e-08, 2.255223824080701e-08]]
    assert_close2d(d2C_dzizjs_expect, gas.d2C_dzizjs(), rtol=1e-13)
    assert_close1d(gas_np.d2C_dzizjs(), d2C_dzizjs_expect, rtol=1e-13)
    assert_close2d(d2C_dzizjs, gas.d2C_dzizjs(), rtol=1e-5)
    assert isinstance(gas_np.d2C_dzizjs(), np.ndarray)

    # Third mole fraction derivatives
    d3C_dzizjzks = hessian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).dC_dzs(), zs, scalar=False, perturbation=3e-5)
    d3C_dzizjzks_expect = [[[8.791135059452836e-09, 1.0308422318043785e-08, 1.1248791844050504e-08], [1.0308422318043785e-08, 1.2261620654600401e-08, 1.3985943446213539e-08], [1.1248791844050504e-08, 1.3985943446213539e-08, 1.685361143642469e-08]], [[1.0308422318043785e-08, 1.2261620654600401e-08, 1.3985943446213539e-08], [1.2261620654600401e-08, 1.4794897903310516e-08, 1.7639492188523473e-08], [1.3985943446213539e-08, 1.7639492188523473e-08, 2.2218641495188888e-08]], [[1.1248791844050504e-08, 1.3985943446213539e-08, 1.685361143642469e-08], [1.3985943446213539e-08, 1.7639492188523473e-08, 2.2218641495188888e-08], [1.685361143642469e-08, 2.2218641495188888e-08, 2.9566930608412272e-08]]]
    assert_close2d(d3C_dzizjzks_expect, gas.d3C_dzizjzks(), rtol=1e-13)
    assert_close1d(gas_np.d3C_dzizjzks(), d3C_dzizjzks_expect, rtol=1e-13)
    assert_close2d(d3C_dzizjzks, gas.d3C_dzizjzks(), rtol=1e-5)
    assert isinstance(gas_np.d3C_dzizjzks(), np.ndarray)

    # Mole number derivatives
    def dC_dns_to_jac(ns):
        zs = normalize(ns)
        return gas.to(T=T, P=P, zs=zs).C()

    dC_dns = jacobian(dC_dns_to_jac, zs, perturbation=2e-7)
    dC_dns_expect =[-1.3867950145098947e-09, -6.385224940753337e-11, 1.4413328290854567e-09]
    assert_close1d(gas.dC_dns(), dC_dns_expect, rtol=1e-13)
    assert_close1d(gas_np.dC_dns(), dC_dns_expect, rtol=1e-13)
    assert isinstance(gas_np.dC_dns(), np.ndarray)
    assert_close1d(dC_dns, gas.dC_dns(), rtol=1e-6)

    # Partial derivatives
    dnC_dns_expect = [1.1409400529001358e-09, 2.463882818002497e-09, 3.969067896495487e-09]
    assert_close1d(gas.dnC_dns(), dnC_dns_expect, rtol=1e-13)
    assert_close1d(gas_np.dnC_dns(), dnC_dns_expect, rtol=1e-13)
    assert isinstance(gas_np.dnC_dns(), np.ndarray)

    dnG_dep_dns_expect = [7.425792779612475, 26.6956451946272, 58.85973416020387]
    assert_close1d(gas.dnG_dep_dns(), dnG_dep_dns_expect, rtol=1e-13)
    assert_close1d(gas_np.dnG_dep_dns(), dnG_dep_dns_expect, rtol=1e-13)
    assert isinstance(gas_np.dnG_dep_dns(), np.ndarray)


    # Volume derivative
    dV_dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).V(), zs, perturbation=3e-6)
    dV_dzs_expect = [-4.112328459328977e-05, -8.550402887152434e-05, -0.0001463861573387482]
    assert_close1d(gas.dV_dzs(), dV_dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.dV_dzs(), dV_dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.dV_dzs(), np.ndarray)
    assert_close1d(dV_dzs, gas.dV_dzs(), rtol=5e-6)

    d2V_dzizjs_expect = [[-1.039580364021386e-05, -3.958861196399791e-05, -7.55799645242824e-05], [-3.958861196399791e-05, -8.314138850953133e-05, -0.00014207163186822304], [-7.55799645242824e-05, -0.00014207163186822304, -0.0002473595232182522]]
    assert_close1d(gas.d2V_dzizjs(), d2V_dzizjs_expect, rtol=1e-13)
    assert_close1d(gas_np.d2V_dzizjs(), d2V_dzizjs_expect, rtol=1e-13)
    assert isinstance(gas_np.d2V_dzizjs(), np.ndarray)

    # Gibbs composition derivative
    dG_dep_dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).G_dep(), zs, perturbation=3e-6)
    dG_dep_dzs_expect = [35.74825427120366, 55.01810668621839, 87.18219565179506]
    assert_close1d(gas.dG_dep_dzs(), dG_dep_dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.dG_dep_dzs(), dG_dep_dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.dG_dep_dzs(), np.ndarray)
    assert_close1d(dG_dep_dzs, gas.dG_dep_dzs(), rtol=5e-6)

    # lnphis call
    def to_jac(ns):
        zs = [i/sum(ns) for i in ns]
        return sum(ns)*gas.to(T=T, P=P, zs=zs).lnphi()
    lnphis = jacobian(to_jac, zs, perturbation=.7e-6)
    lnphis_expect = [0.0029770586188775424, 0.010702493723142819, 0.023597329482121697]
    assert_close1d(gas.lnphis(), lnphis_expect, rtol=1e-13)
    assert_close1d(gas_np.lnphis(), lnphis_expect, rtol=1e-13)
    assert isinstance(gas_np.lnphis(), np.ndarray)
    assert_close1d(lnphis, gas.lnphis(), rtol=5e-7)


    # B and C pressure derivatives at constant volume
    dB_dP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).B(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.dB_dP_V(), 1.1842641665356751e-09, rtol=1e-11)
    assert_close(gas.dB_dP_V(), dB_dP_V)

    d2B_dTdP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).dB_dT(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.d2B_dTdP_V(), -1.04152429353394e-11, rtol=1e-11)
    assert_close(gas.d2B_dTdP_V(), d2B_dTdP_V)

    d3B_dT2dP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).d2B_dT2(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.d3B_dT2dP_V(), 1.2761099797481876e-13, rtol=1e-11)
    assert_close(gas.d3B_dT2dP_V(), d3B_dT2dP_V)

    dC_dP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).C(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.dC_dP_V(), -2.5264916071048583e-14)
    assert_close(gas.dC_dP_V(), dC_dP_V)

    d2C_dTdP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).dC_dT(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.d2C_dTdP_V(), 2.9635255492502356e-16, rtol=1e-11)
    assert_close(gas.d2C_dTdP_V(), d2C_dTdP_V)

    d3C_dT2dP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).d2C_dT2(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.d3C_dT2dP_V(),-3.891861385232169e-18, rtol=1e-11)
    assert_close(gas.d3C_dT2dP_V(), d3C_dT2dP_V)

    # B and C volume derivatives at constant pressure
    dB_dV_P = derivative(lambda V: gas.to(V=V, P=gas.P, zs=gas.zs).B(), gas.V(), dx=gas.V()*1e-6)
    assert_close(gas.dB_dV_P(), 0.00474781253771208, rtol=1e-11)
    assert_close(gas.dB_dV_P(), dB_dV_P)

    d2B_dTdV_P = derivative(lambda V: gas.to(V=V, P=gas.P, zs=gas.zs).dB_dT(), gas.V(), dx=gas.V()*1e-6)
    assert_close(gas.d2B_dTdV_P(),-4.175556635845567e-05, rtol=1e-11)
    assert_close(gas.d2B_dTdV_P(), d2B_dTdV_P)

    d3B_dT2dV_P = derivative(lambda V: gas.to(V=V, P=gas.P, zs=gas.zs).d2B_dT2(), gas.V(), dx=gas.V()*1e-6)
    assert_close(gas.d3B_dT2dV_P(), 5.116029964050627e-07, rtol=1e-11)
    assert_close(gas.d3B_dT2dV_P(), d3B_dT2dV_P)

    dC_dV_P = derivative(lambda V: gas.to(V=V, P=gas.P, zs=gas.zs).C(), gas.V(), dx=gas.V()*1e-6)
    assert_close(gas.dC_dV_P(), -1.0128912845287412e-07)
    assert_close(gas.dC_dV_P(), dC_dV_P)

    d2C_dTdV_P = derivative(lambda V: gas.to(V=V, P=gas.P, zs=gas.zs).dC_dT(), gas.V(), dx=gas.V()*1e-6)
    assert_close(gas.d2C_dTdV_P(), 1.1881017897991506e-09, rtol=1e-11)
    assert_close(gas.d2C_dTdV_P(), d2C_dTdV_P)

    d3C_dT2dV_P = derivative(lambda V: gas.to(V=V, P=gas.P, zs=gas.zs).d2C_dT2(), gas.V(), dx=gas.V()*1e-6)
    assert_close(gas.d3C_dT2dV_P(), -1.5602792689316894e-11, rtol=1e-11)
    assert_close(gas.d3C_dT2dV_P(), d3C_dT2dV_P)



    # Enthalpy extra derivatives

    dH_dep_dP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).H_dep(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.dH_dep_dP_V(), -0.000150298196047701, rtol=1e-11)
    assert_close(gas.dH_dep_dP_V(), dH_dep_dP_V)

    dH_dP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).H(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.dH_dP_V(), 0.10683350766127504, rtol=1e-11)
    assert_close(gas.dH_dP_V(), dH_dP_V)

    dH_dep_dP_T = derivative(lambda P: gas.to(T=gas.T, P=P, zs=gas.zs).H_dep(), gas.P, dx=gas.P*6e-7)
    assert_close(gas.dH_dep_dP_T(), 0.00016339614251889556, rtol=1e-11)
    assert_close(gas.dH_dep_dP_T(), dH_dep_dP_T)

    dH_dP_T = derivative(lambda P: gas.to(T=gas.T, P=P, zs=gas.zs).H(), gas.P, dx=gas.P*6e-7)
    assert_close(gas.dH_dP_T(), 0.00016339614251889556, rtol=1e-11)
    assert_close(gas.dH_dP_T(), dH_dP_T)

    dH_dep_dV_T = derivative(lambda V: gas.to(T=gas.T, V=V, zs=gas.zs).H_dep(), gas.V(), dx=gas.V()*6e-7)
    assert_close(gas.dH_dep_dV_T(), -655.0685868798632, rtol=1e-11)
    assert_close(gas.dH_dep_dV_T(), dH_dep_dV_T)

    dH_dV_T = derivative(lambda V: gas.to(T=gas.T, V=V, zs=gas.zs).H(), gas.V(), dx=gas.V()*6e-7)
    assert_close(gas.dH_dV_T(), -655.0685868798632, rtol=1e-11)
    assert_close(gas.dH_dV_T(), dH_dV_T)

    dH_dep_dV_P = derivative(lambda V: gas.to(P=gas.P, V=V, zs=gas.zs).H_dep(), gas.V(), dx=gas.V()*6e-7)
    assert_close(gas.dH_dep_dV_P(), -1257.6264280735763, rtol=1e-11)
    assert_close(gas.dH_dep_dV_P(), dH_dep_dV_P)

    dH_dV_P = derivative(lambda V: gas.to(P=gas.P, V=V, zs=gas.zs).H(), gas.V(), dx=gas.V()*6e-7)
    assert_close(gas.dH_dV_P(), 427649.25865266385, rtol=1e-11)
    assert_close(gas.dH_dV_P(), dH_dV_P)

    # entropy special derivatives

    dS_dep_dP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).S_dep(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.dS_dep_dP_V(), 6.480190482120792e-07, rtol=1e-11)
    assert_close(gas.dS_dep_dP_V(), dS_dep_dP_V)

    dS_dP_V = derivative(lambda P: gas.to(V=gas.V(), P=P, zs=gas.zs).S(), gas.P, dx=gas.P*1e-6)
    assert_close(gas.dS_dP_V(), 0.0002741160790577555, rtol=1e-11)
    assert_close(gas.dS_dP_V(), dS_dP_V)

    dS_dep_dP_T = derivative(lambda P: gas.to(T=gas.T, P=P, zs=gas.zs).S_dep(), gas.P, dx=gas.P*6e-7)
    assert_close(gas.dS_dep_dP_T(),-3.9762874700991293e-07, rtol=1e-11)
    assert_close(gas.dS_dep_dP_T(), dS_dep_dP_T)

    dS_dP_T = derivative(lambda P: gas.to(T=gas.T, P=P, zs=gas.zs).S(), gas.P, dx=gas.P*6e-7)
    assert_close(gas.dS_dP_T(), -8.354225492854231e-05, rtol=1e-11)
    assert_close(gas.dS_dP_T(), dS_dP_T)


    dS_dep_dV_T = derivative(lambda V: gas.to(T=gas.T, V=V, zs=gas.zs).S_dep(), gas.V(), dx=gas.V()*6e-7)
    assert_close(gas.dS_dep_dV_T(), 1.5941263813891449, rtol=1e-11)
    assert_close(gas.dS_dep_dV_T(), dS_dep_dV_T)

    dS_dV_T = derivative(lambda V: gas.to(T=gas.T, V=V, zs=gas.zs).S(), gas.V(), dx=gas.V()*6e-7)
    assert_close(gas.dS_dV_T(), 334.92777759101733, rtol=1e-11)
    assert_close(gas.dS_dV_T(), dS_dV_T)

    dS_dep_dV_P = derivative(lambda V: gas.to(P=gas.P, V=V, zs=gas.zs).S_dep(), gas.V(), dx=gas.V()*6e-7)
    assert_close(gas.dS_dep_dV_P(), 4.192088093578603, rtol=1e-11)
    assert_close(gas.dS_dep_dV_P(), dS_dep_dV_P)

    # This is WRONG, the Cp T integral needs work, TODO
    # dS_dV_P = derivative(lambda V: gas.to(P=gas.P, V=V, zs=gas.zs).S(), gas.V(), dx=gas.V()*6e-3)
    # assert_close(gas.dS_dV_P(), 427649.25865266385, rtol=1e-11)
    # assert_close(gas.dS_dV_P(), dS_dV_P)

    dS_dep_dT_V = derivative(lambda T: gas.to(T=T, V=gas.V(), zs=gas.zs).S_dep(), gas.T, dx=gas.T*6e-7)
    assert_close(gas.dS_dep_dT_V(), 0.00021703957965431793, rtol=1e-11)
    assert_close(gas.dS_dep_dT_V(), dS_dep_dT_V)

    dS_dT_V = derivative(lambda T: gas.to(T=T, V=gas.V(), zs=gas.zs).S(), gas.T, dx=gas.T*6e-7)
    assert_close(gas.dS_dT_V(), 0.09180908916077761, rtol=1e-11)
    assert_close(gas.dS_dT_V(), dS_dT_V)

# test_virial_phase_ternary_BC_pitzer_curl_orbey_vera()


def test_virial_phase_ternary_BC_pitzer_curl_orbey_vera_no_interactions():
    CASs = ['7727-37-9', '74-82-8', '124-38-9']
    atomss = [{'N': 2}, {'C': 1, 'H': 4}, {'C': 1, 'O': 2}]
    Tcs = [126.2, 190.564, 304.2]
    Tcs_np = np.array(Tcs)
    Pcs = [3394387.5, 4599000.0, 7376460.0]
    Pcs_np = np.array(Pcs)
    Vcs = [8.95e-05, 9.86e-05, 9.4e-05]
    Vcs_np = np.array(Vcs)
    omegas = [0.04, 0.008, 0.2252]
    omegas_np = np.array(omegas)
    N = 3

    HeatCapacityGases = [HeatCapacityGas(CASRN="7727-37-9", MW=28.0134, similarity_variable=0.07139440410660612, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-6.496329615255804e-23, 2.1505678500404716e-19, -2.2204849352453665e-16, 1.7454757436517406e-14, 9.796496485269412e-11, -4.7671178529502835e-08, 8.384926355629239e-06, -0.0005955479316119903, 29.114778709934264])),
     HeatCapacityGas(CASRN="74-82-8", MW=16.04246, similarity_variable=0.3116728980468083, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [6.7703235945157e-22, -2.496905487234175e-18, 3.141019468969792e-15, -8.82689677472949e-13, -1.3709202525543862e-09, 1.232839237674241e-06, -0.0002832018460361874, 0.022944239587055416, 32.67333514157593])),
     HeatCapacityGas(CASRN="124-38-9", MW=44.0095, similarity_variable=0.0681671002851657, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-3.1115474168865828e-21, 1.39156078498805e-17, -2.5430881416264243e-14, 2.4175307893014295e-11, -1.2437314771044867e-08, 3.1251954264658904e-06, -0.00021220221928610925, 0.000884685506352987, 29.266811602924644]))]

    # Specifically test without kijs
    kijs = [[0.0]*3 for _ in range(N)]
    kijs_np = np.array(kijs)

    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas,
                                  B_model=VIRIAL_B_ABBOTT,
                                  cross_B_model=VIRIAL_B_ABBOTT,
                                  cross_B_model_kijs=kijs,
                                  C_model=VIRIAL_C_ORBEY_VERA)
    model_np = VirialCSP(Tcs=Tcs_np, Pcs=Pcs_np, Vcs=Vcs_np, omegas=omegas_np,
                              B_model=VIRIAL_B_ABBOTT,
                              cross_B_model=VIRIAL_B_ABBOTT,
                              cross_B_model_kijs=kijs_np,
                              C_model=VIRIAL_C_ORBEY_VERA)


    T = 300
    P = 1e5
    zs = [.02, .92, .06]
    zs_np = np.array(zs)
    gas = VirialGas(model=model, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='linear', cross_C_model='linear',
                T=T, P=P, zs=zs)
    gas_np = VirialGas(model=model_np, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='linear',cross_C_model='linear',
                T=T, P=P, zs=zs_np)


    # Test some basics
    B, dB, d2B, d3B = (-4.559350248793301e-05, 4.0870609295718214e-07, -3.6526254783551437e-09, 4.574694317862753e-11)
    assert_close(gas.B(), B, rtol=1e-13)
    assert_close(gas.dB_dT(), dB, rtol=1e-13)
    assert_close(gas.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas.d3B_dT3(), d3B, rtol=1e-13)
    assert_close(gas_np.B(), B, rtol=1e-13)
    assert_close(gas_np.dB_dT(), dB, rtol=1e-13)
    assert_close(gas_np.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas_np.d3B_dT3(), d3B, rtol=1e-13)

    B_mat = gas.model.B_interactions()
    assert_close1d(gas.model.B_pures(), [B_mat[i][i] for i in range(N)], rtol=1e-13)


    for val in (gas.B(), gas.dB_dT(), gas.d2B_dT2(), gas.d3B_dT3(),
                gas_np.B(), gas_np.dB_dT(), gas_np.d2B_dT2(), gas_np.d3B_dT3()):
        assert type(val) is float


    for val in (gas.C(), gas.dC_dT(), gas.d2C_dT2(), gas.d3C_dT3(),
                gas_np.C(), gas_np.dC_dT(), gas_np.d2C_dT2(), gas_np.d3C_dT3()):
        assert type(val) is float

    V = 0.0248978152556876
    assert_close(gas.V(), V, rtol=1e-13)
    assert_close(gas_np.V(), V, rtol=1e-13)


    # Check the B and T derivatives
    assert_close(gas.dB_dT(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).B(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d2B_dT2(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).dB_dT(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d3B_dT3(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).d2B_dT2(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.dC_dT(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).C(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d2C_dT2(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).dC_dT(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas.d3C_dT3(), derivative(lambda T: gas.to(T=T, P=P, zs=zs).d2C_dT2(), gas.T, gas.T*3e-7), rtol=1e-7)
    assert_close(gas_np.dB_dT(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).B(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d2B_dT2(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).dB_dT(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d3B_dT3(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).d2B_dT2(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.dC_dT(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).C(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d2C_dT2(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).dC_dT(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)
    assert_close(gas_np.d3C_dT3(), derivative(lambda T: gas_np.to(T=T, P=P, zs=zs).d2C_dT2(), gas_np.T, gas_np.T*3e-7), rtol=1e-7)


    # Mole fraction derivatives of C
    dC_dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).C(), zs, perturbation=1e-7)
    dC_dzs_expect =  [1.4651891765754702e-09, 2.4658163172184143e-09, 4.927821768068704e-09]
    assert_close1d(gas.dC_dzs(), dC_dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.dC_dzs(), dC_dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.dC_dzs(), np.ndarray)
    assert_close1d(dC_dzs, gas.dC_dzs())


    # Second mole fraction derivatives
    d2C_dzizjs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).dC_dzs(), zs, scalar=False, perturbation=125e-5)
    d2C_dzizjs_expect = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
    assert_close2d(d2C_dzizjs_expect, gas.d2C_dzizjs(), rtol=1e-13)
    assert_close1d(gas_np.d2C_dzizjs(), d2C_dzizjs_expect, rtol=1e-13)
    assert_close2d(d2C_dzizjs, gas.d2C_dzizjs(), rtol=1e-6)
    assert isinstance(gas_np.d2C_dzizjs(), np.ndarray)


    # Third mole fraction derivatives
    d3C_dzizjzks_expect = [[[0.0]*N for _ in range(N)] for _ in range(N)]
    assert_close3d(d3C_dzizjzks_expect, gas.d3C_dzizjzks(), atol=0)


    # Mole number derivatives
    def dC_dns_to_jac(ns):
        zs = normalize(ns)
        return gas.to(T=T, P=P, zs=zs).C()

    dC_dns = jacobian(dC_dns_to_jac, zs, perturbation=.5e-7)
    dC_dns_expect = [-1.1283349248811026e-09, -1.277077842381585e-10, 2.334297666612131e-09]
    assert_close1d(gas.dC_dns(), dC_dns_expect, rtol=1e-13)
    assert_close1d(gas_np.dC_dns(), dC_dns_expect, rtol=1e-13)
    assert isinstance(gas_np.dC_dns(), np.ndarray)
    assert_close1d(dC_dns, gas.dC_dns(), rtol=5e-7)

    # Partial derivatives
    dnC_dns_expect = [1.4651891765754702e-09, 2.4658163172184143e-09, 4.927821768068704e-09]
    assert_close1d(gas.dnC_dns(), dnC_dns_expect, rtol=1e-13)
    assert_close1d(gas_np.dnC_dns(), dnC_dns_expect, rtol=1e-13)
    assert isinstance(gas_np.dnC_dns(), np.ndarray)

    dV_dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).V(), zs, perturbation=20e-6)
    dV_dzs_expect = [-5.28010263028751e-06, -4.14836598615557e-05, -0.00012309010527128904]
    assert_close1d(gas.dV_dzs(), dV_dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.dV_dzs(), dV_dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.dV_dzs(), np.ndarray)
    assert_close1d(dV_dzs, gas.dV_dzs(), rtol=5e-6)

    dG_dep_dzs = jacobian(lambda zs: gas.to(T=gas.T, P=gas.P, zs=zs).G_dep(), zs, perturbation=3e-6)
    dG_dep_dzs_expect = [11.039653592654977, 26.791676731271732, 71.69351860968281]
    assert_close1d(gas.dG_dep_dzs(), dG_dep_dzs_expect, rtol=1e-13)
    assert_close1d(gas_np.dG_dep_dzs(), dG_dep_dzs_expect, rtol=1e-13)
    assert isinstance(gas_np.dG_dep_dzs(), np.ndarray)
    assert_close1d(dG_dep_dzs, gas.dG_dep_dzs(), rtol=5e-6)

    def to_jac(ns):
        zs = [i/sum(ns) for i in ns]
        return sum(ns)*gas.to(T=T, P=P, zs=zs).lnphi()
    lnphis = jacobian(to_jac, zs, perturbation=4e-6)
    lnphis_expect = [0.0044028238165937945, 0.010717933548490296, 0.028719434443398972]
    assert_close1d(gas.lnphis(), lnphis_expect, rtol=1e-13)
    assert_close1d(gas_np.lnphis(), lnphis_expect, rtol=1e-13)
    assert isinstance(gas_np.lnphis(), np.ndarray)
    assert_close1d(lnphis, gas.lnphis(), rtol=1e-6)

# test_virial_phase_ternary_BC_pitzer_curl_orbey_vera_no_interactions()

def test_ternary_virial_phase_hashing_repr():
    Tcs=[126.2, 154.58, 150.8]
    Pcs=[3394387.5, 5042945.25, 4873732.5]
    Vcs=[8.95e-05, 7.34e-05, 7.49e-05]
    omegas=[0.04, 0.021, -0.004]
    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas, B_model='VIRIAL_B_PITZER_CURL', cross_B_model='Tarakad-Danner', C_model='VIRIAL_C_ORBEY_VERA')
    HeatCapacityGases = [HeatCapacityGas(poly_fit=(50.0, 1000.0, [R*1.79e-12, R*-6e-09, R*6.58e-06, R*-0.001794, R*3.63])),
                         HeatCapacityGas(poly_fit=(50.0, 1000.0, [R*-9.9e-13, R*1.57e-09, R*7e-08, R*-0.000261, R*3.539])),
                         HeatCapacityGas(poly_fit=(50.0, 1000.0, [0,0,0,0, R*2.5]))]
    phase = VirialGas(model=model, T=300, P=1e5, zs=[.78, .21, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')



    model2 = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas, B_model='VIRIAL_B_PITZER_CURL', cross_B_model='Tarakad-Danner', C_model='VIRIAL_C_ORBEY_VERA')
    phase2 = VirialGas(model=model2, T=300, P=1e5, zs=[.78, .21, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')

    # print(model.__dict__)
    # print(model2.__dict__)
    assert model.model_hash() == model2.model_hash()

    modeld0 = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas, B_model='VIRIAL_B_ABBOTT', cross_B_model='Tarakad-Danner', C_model='VIRIAL_C_ORBEY_VERA')

    # Change T
    phased0 = VirialGas(model=model, T=315, P=1e5, zs=[.78, .21, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')
    # Change P
    phased1 = VirialGas(model=model, T=300, P=1e6, zs=[.78, .21, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')
    # Change composition
    phased2 = VirialGas(model=model2, T=300, P=1e5, zs=[.77, .22, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')
    # Change model to the same model
    phased3 = VirialGas(model=model2, T=300, P=1e5, zs=[.78, .21, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')

    # Make a variety of diffinitely different models
    phased4 = VirialGas(model=modeld0, T=315, P=1e5, zs=[.78, .21, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')
    phased5 = VirialGas(model=modeld0, T=300, P=1e6, zs=[.78, .21, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')
    phased6 = VirialGas(model=modeld0, T=300, P=1e5, zs=[.77, .22, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz')
    phased7 = VirialGas(model=modeld0, T=300, P=1e5, zs=[.78, .21, .01], HeatCapacityGases=HeatCapacityGases, cross_B_model='linear', cross_C_model='Orentlicher-Prausnitz')


    assert model.state_hash() == model2.state_hash()
    assert model == model2
    assert hash(model) == hash(model2)
    assert eval(repr(model)) == model


    assert phase.model_hash() == phase2.model_hash()
    assert phase.state_hash() == phase2.state_hash()
    assert phase == phase2
    assert hash(phase) == hash(phase2)
    assert eval(repr(phase2)) == phase

    for p in (phased3, phased0, phased1, phased2):
        assert phase.model_hash() == p.model_hash()
    for p in (phased0, phased1, phased2):
        assert not phase.state_hash() == p.state_hash()
        assert not phase == p
        assert not hash(phase) == hash(p)

    for p in (phased4, phased5, phased6, phased7):
        assert not phase.model_hash() == p.model_hash()
        assert not phase.state_hash() == p.state_hash()
        assert not phase == p
        assert not hash(phase) == hash(p)

# test_ternary_virial_phase_hashing_repr()


def test_virial_ternary_vs_ideal_gas():
    constants = ChemicalConstantsPackage(Tcs=[508.1, 536.2, 512.5], Pcs=[4700000.0, 5330000.0, 8084000.0], omegas=[0.309, 0.21600000000000003, 0.5589999999999999], Vcs=[0.000213, 0.000244, 0.000117],
                                    MWs=[58.07914, 119.37764000000001, 32.04186], CASs=['67-64-1', '67-66-3', '67-56-1'], names=['acetone', 'chloroform', 'methanol'])
    HeatCapacityGases = [HeatCapacityGas(poly_fit=(200.0, 1000.0, [-1.3320002425347943e-21, 6.4063345232664645e-18, -1.251025808150141e-14, 1.2265314167534311e-11, -5.535306305509636e-09, -4.32538332013644e-08, 0.0010438724775716248, -0.19650919978971002, 63.84239495676709])),
                        HeatCapacityGas(poly_fit=(200.0, 1000.0, [1.5389278550737367e-21, -8.289631533963465e-18, 1.9149760160518977e-14, -2.470836671137373e-11, 1.9355882067011222e-08, -9.265600540761629e-06, 0.0024825718663005762, -0.21617464276832307, 48.149539665907696])),
                        HeatCapacityGas(poly_fit=(50.0, 1000.0, [2.3511458696647882e-21, -9.223721411371584e-18, 1.3574178156001128e-14, -8.311274917169928e-12, 4.601738891380102e-10, 1.78316202142183e-06, -0.0007052056417063217, 0.13263597297874355, 28.44324970462924]))]
    correlations = PropertyCorrelationsPackage(constants=constants, skip_missing=True, HeatCapacityGases=HeatCapacityGases)


    T, P = 350.0, 1e6
    zs = [0.2, 0.3, 0.5]
    N = len(zs)
    phase = IdealGas(T=T, P=P, zs=zs, HeatCapacityGases=HeatCapacityGases)

    model = VirialCSP(Tcs=constants.Tcs, Pcs=constants.Pcs, Vcs=constants.Vcs, omegas=constants.omegas, B_model=VIRIAL_B_ZERO, C_model=VIRIAL_C_ZERO)
    phase_EOS = VirialGas(model, HeatCapacityGases=HeatCapacityGases, T=T, P=P, zs=zs)


    ### Copy specs
    TV = phase.to(T=T, V=phase.V(), zs=zs)
    assert_close(TV.P, phase.P, rtol=1e-12)
    PV = phase.to(P=P, V=phase.V(), zs=zs)
    assert_close(TV.T, phase.T, rtol=1e-12)

    new = phase.to_TP_zs(T=350.0, P=1e6, zs=zs)
    assert new.T == phase.T
    assert new.P == phase.P

    new = phase.to(P=P, T=T, zs=zs)
    assert new.T == phase.T
    assert new.P == phase.P
    with pytest.raises(Exception):
        phase.to(P=P, zs=zs)

    B_mat = phase_EOS.model.B_interactions()
    assert_close1d(phase_EOS.model.B_pures(), [B_mat[i][i] for i in range(N)], rtol=1e-13)
    C_mat = phase_EOS.model.C_interactions()
    assert_close1d(phase_EOS.model.C_pures(), [C_mat[i][i] for i in range(N)], rtol=1e-13)

    assert_close(phase.V(), phase_EOS.V(), rtol=1e-11)
    assert_close(phase.PIP(), phase_EOS.PIP(), rtol=1e-11)

    ### Derivatives of volume
    assert_close(phase.dV_dT(), phase_EOS.dV_dT(), rtol=1e-11)
    assert_close(phase.dV_dT_P(), phase_EOS.dV_dT_P(), rtol=1e-11)

    assert_close(phase.dV_dP(), phase_EOS.dV_dP(), rtol=1e-11)
    assert_close(phase.dV_dP_T(), phase_EOS.dV_dP_T(), rtol=1e-11)

    assert_close(phase.d2V_dT2(), phase_EOS.d2V_dT2(), rtol=1e-11)
    assert_close(phase.d2V_dT2_P(), phase_EOS.d2V_dT2_P(), rtol=1e-11)

    assert_close(phase.d2V_dP2(), phase_EOS.d2V_dP2(), rtol=1e-11)
    assert_close(phase.d2V_dP2_T(), phase_EOS.d2V_dP2_T(), rtol=1e-11)

    assert_close(phase.d2V_dTdP(), phase_EOS.d2V_dTdP(), rtol=1e-11)
    assert_close(phase.d2V_dPdT(), phase_EOS.d2V_dPdT(), rtol=1e-11)

    assert phase.dZ_dT() == 0.0
    assert phase.dZ_dP() == 0.0

    assert_close1d(phase_EOS.dV_dzs(), [0.0, 0.0, 0.0], atol=0.0, rtol=0.0)
    assert_close1d(phase_EOS.dZ_dzs(), [0.0, 0.0, 0.0], atol=0.0, rtol=0.0)
    assert_close1d(phase_EOS.dV_dzs(), [0.0, 0.0, 0.0], atol=0.0, rtol=0.0)
    assert_close1d(phase_EOS.dZ_dzs(), [0.0, 0.0, 0.0], atol=0.0, rtol=0.0)


    ### Derivatives of pressure
    assert_close(phase.dP_dV(), phase_EOS.dP_dV(), rtol=1e-11)
    assert_close(phase.dP_dV_T(), phase_EOS.dP_dV_T(), rtol=1e-11)

    assert_close(phase.dP_dT(), phase_EOS.dP_dT(), rtol=1e-11)
    assert_close(phase.dP_dT_V(), phase_EOS.dP_dT_V(), rtol=1e-11)

    assert_close(phase.d2P_dV2(), phase_EOS.d2P_dV2(), rtol=1e-11)
    assert_close(phase.d2P_dV2_T(), phase_EOS.d2P_dV2_T(), rtol=1e-11)

    assert_close(phase.d2P_dT2(), phase_EOS.d2P_dT2(), rtol=1e-11)
    assert_close(phase.d2P_dT2_V(), phase_EOS.d2P_dT2_V(), rtol=1e-11)

    assert_close(phase.d2P_dVdT(), phase_EOS.d2P_dVdT(), rtol=1e-11)
    assert_close(phase.d2P_dTdV(), phase_EOS.d2P_dTdV(), rtol=1e-11)

    ### Derivatives of Temperature
    assert_close(phase.dT_dV(), phase_EOS.dT_dV(), rtol=1e-11)
    assert_close(phase.dT_dV_P(), phase_EOS.dT_dV_P(), rtol=1e-11)

    assert_close(phase.dT_dP(), phase_EOS.dT_dP(), rtol=1e-11)
    assert_close(phase.dT_dP_V(), phase_EOS.dT_dP_V(), rtol=1e-11)

    assert_close(phase.d2T_dV2(), phase_EOS.d2T_dV2(), rtol=1e-11)
    assert_close(phase.d2T_dV2_P(), phase_EOS.d2T_dV2_P(), rtol=1e-11)

    assert_close(phase.d2T_dP2(), phase_EOS.d2T_dP2(), rtol=1e-11)
    assert_close(phase.d2T_dP2_V(), phase_EOS.d2T_dP2_V(), rtol=1e-11)

    assert_close(phase.d2T_dVdP(), phase_EOS.d2T_dVdP(), rtol=1e-11)
    assert_close(phase.d2T_dPdV(), phase_EOS.d2T_dPdV(), rtol=1e-11)


    ### Phis and derivatives
    assert_close1d(phase.phis(), phase_EOS.phis(), rtol=1e-11)
    assert_close1d(phase.lnphis(), phase_EOS.lnphis(), rtol=1e-11)
    assert_close1d(phase.fugacities(), phase_EOS.fugacities(), rtol=1e-11)

    # Basic thermodynamic quantities
    assert_close(phase.H(), phase_EOS.H(), rtol=1e-11)
    assert_close(phase.S(), phase_EOS.S(), rtol=1e-11)
    assert_close(phase.G(), phase_EOS.G(), rtol=1e-11)
    assert_close(phase.U(), phase_EOS.U(), rtol=1e-11)
    assert_close(phase.A(), phase_EOS.A(), rtol=1e-11)

    ### First temperature derivative

    assert_close(phase.dH_dT(), phase_EOS.dH_dT(), rtol=1e-11)
    assert_close(phase.dS_dT(), phase_EOS.dS_dT(), rtol=1e-11)
    assert_close(phase.dG_dT(), phase_EOS.dG_dT(), rtol=1e-11)
    assert_close(phase.dU_dT(), phase_EOS.dU_dT(), rtol=1e-11)
    assert_close(phase.dA_dT(), phase_EOS.dA_dT(), rtol=1e-11)

    assert_close(phase.dH_dT_P(), phase_EOS.dH_dT_P(), rtol=1e-11)
    assert_close(phase.dS_dT_P(), phase_EOS.dS_dT_P(), rtol=1e-11)
    assert_close(phase.dG_dT_P(), phase_EOS.dG_dT_P(), rtol=1e-11)
    assert_close(phase.dU_dT_P(), phase_EOS.dU_dT_P(), rtol=1e-11)
    assert_close(phase.dA_dT_P(), phase_EOS.dA_dT_P(), rtol=1e-11)

    assert_close(phase.dH_dT_V(), phase_EOS.dH_dT_V(), rtol=1e-11)
    assert_close(phase.dS_dT_V(), phase_EOS.dS_dT_V(), rtol=1e-11)
    assert_close(phase.dG_dT_V(), phase_EOS.dG_dT_V(), rtol=1e-11)
    assert_close(phase.dU_dT_V(), phase_EOS.dU_dT_V(), rtol=1e-11)
    assert_close(phase.dA_dT_V(), phase_EOS.dA_dT_V(), rtol=1e-11)

    ### First pressure derivative
    assert_close(phase.dH_dP(), phase_EOS.dH_dP(), rtol=1e-11, atol=1e-16)
    assert_close(phase.dS_dP(), phase_EOS.dS_dP(), rtol=1e-11)
    assert_close(phase.dG_dP(), phase_EOS.dG_dP(), rtol=1e-11)
    assert_close(phase.dU_dP(), phase_EOS.dU_dP(), rtol=1e-11, atol=1e-16)
    assert_close(phase.dA_dP(), phase_EOS.dA_dP(), rtol=1e-11)

    assert_close(phase.dH_dP_T(), phase_EOS.dH_dP_T(), rtol=1e-11, atol=1e-16)
    assert_close(phase.dS_dP_T(), phase_EOS.dS_dP_T(), rtol=1e-11)
    assert_close(phase.dG_dP_T(), phase_EOS.dG_dP_T(), rtol=1e-11)
    assert_close(phase.dU_dP_T(), phase_EOS.dU_dP_T(), rtol=1e-11, atol=1e-16)
    assert_close(phase.dA_dP_T(), phase_EOS.dA_dP_T(), rtol=1e-11)

    assert_close(phase.dH_dP_V(), phase_EOS.dH_dP_V(), rtol=1e-11)
    assert_close(phase.dS_dP_V(), phase_EOS.dS_dP_V(), rtol=1e-11)
    assert_close(phase.dG_dP_V(), phase_EOS.dG_dP_V(), rtol=1e-11)
    assert_close(phase.dU_dP_V(), phase_EOS.dU_dP_V(), rtol=1e-11, atol=1e-16)
    assert_close(phase.dA_dP_V(), phase_EOS.dA_dP_V(), rtol=1e-11)

    assert_close(phase.dH_dV_T(), phase_EOS.dH_dV_T(), rtol=1e-11)
    assert_close(phase.dS_dV_T(), phase_EOS.dS_dV_T(), rtol=1e-11)
    assert_close(phase.dG_dV_T(), phase_EOS.dG_dV_T(), rtol=1e-11)

    assert phase_EOS.H_dep() == 0
    assert phase_EOS.S_dep() == 0
    assert phase_EOS.G_dep() == 0
    assert phase_EOS.U_dep() == 0
    assert phase_EOS.A_dep() == 0
# test_virial_ternary_vs_ideal_gas()

def test_virial_easy_B_C_models():
    from chemicals.virial import Meng_virial_a
    CASs = ['7727-37-9', '74-82-8', '124-38-9']
    atomss = [{'N': 2}, {'C': 1, 'H': 4}, {'C': 1, 'O': 2}]
    Tcs = [126.2, 190.564, 304.2]
    Pcs = [3394387.5, 4599000.0, 7376460.0]
    Vcs = [8.95e-05, 9.86e-05, 9.4e-05]
    omegas = [0.04, 0.008, 0.2252]
    N = 3

    HeatCapacityGases = [HeatCapacityGas(CASRN="7727-37-9", MW=28.0134, similarity_variable=0.07139440410660612, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-6.496329615255804e-23, 2.1505678500404716e-19, -2.2204849352453665e-16, 1.7454757436517406e-14, 9.796496485269412e-11, -4.7671178529502835e-08, 8.384926355629239e-06, -0.0005955479316119903, 29.114778709934264])),
     HeatCapacityGas(CASRN="74-82-8", MW=16.04246, similarity_variable=0.3116728980468083, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [6.7703235945157e-22, -2.496905487234175e-18, 3.141019468969792e-15, -8.82689677472949e-13, -1.3709202525543862e-09, 1.232839237674241e-06, -0.0002832018460361874, 0.022944239587055416, 32.67333514157593])),
     HeatCapacityGas(CASRN="124-38-9", MW=44.0095, similarity_variable=0.0681671002851657, extrapolation="linear", method="POLY_FIT", poly_fit=(50.0, 1000.0, [-3.1115474168865828e-21, 1.39156078498805e-17, -2.5430881416264243e-14, 2.4175307893014295e-11, -1.2437314771044867e-08, 3.1251954264658904e-06, -0.00021220221928610925, 0.000884685506352987, 29.266811602924644]))]

    kijs = [[0.0, 0.024878043016556484, 0.0],
     [0.024878043016556484, 0.0, 0.04313694538361394],
     [0.0, 0.04313694538361394, 0.0]]

    # Get VIRIAL_B_TSONOPOULOS and VIRIAL_C_XIANG
    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas,
                                  B_model=VIRIAL_B_TSONOPOULOS,
                                  cross_B_model=VIRIAL_B_TSONOPOULOS,
                                  cross_B_model_kijs=kijs,
                                  C_model=VIRIAL_C_XIANG)
    T = 300
    P = 1e5
    zs = [.02, .92, .06]
    gas = VirialGas(model=model, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz',
                T=T, P=P, zs=zs)
    B, dB, d2B, d3B = -4.434925694919992e-05, 3.937650137082095e-07, -3.2111514855184275e-09, 3.870025170366373e-11
    assert_close(gas.B(), B, rtol=1e-13)
    assert_close(gas.dB_dT(), dB, rtol=1e-13)
    assert_close(gas.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas.d3B_dT3(), d3B, rtol=1e-13)

    C, dC, d2C, d3C = (2.3672028792220187e-09, -8.499704687487288e-12, 1.014506083545108e-13, -1.3762462745837535e-15)
    assert_close(gas.C(), C, rtol=1e-13)
    assert_close(gas.dC_dT(), dC, rtol=1e-13)
    assert_close(gas.d2C_dT2(), d2C, rtol=1e-13)
    assert_close(gas.d3C_dT3(), d3C, rtol=1e-13)

    B_mat = gas.model.B_interactions()
    assert_close1d(gas.model.B_pures(), [B_mat[i][i] for i in range(N)], rtol=1e-13)
    C_mat = gas.model.C_interactions()
    assert_close1d(gas.model.C_pures(), [C_mat[i][i] for i in range(N)], rtol=1e-13)

    # VIRIAL_B_OCONNELL_PRAUSNITZ

    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas,
                                  B_model=VIRIAL_B_OCONNELL_PRAUSNITZ,
                                  cross_B_model=VIRIAL_B_OCONNELL_PRAUSNITZ,
                                  cross_B_model_kijs=kijs,
                                  C_model=VIRIAL_C_ZERO)
    gas = VirialGas(model=model, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz',
                T=T, P=P, zs=zs)

    B, dB, d2B, d3B = -4.4070589761655625e-05, 3.9458357396975057e-07, -3.265768989707745e-09, 4.069493510858296e-11
    assert_close(gas.B(), B, rtol=1e-13)
    assert_close(gas.dB_dT(), dB, rtol=1e-13)
    assert_close(gas.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas.d3B_dT3(), d3B, rtol=1e-13)
    B_mat = gas.model.B_interactions()
    assert_close1d(gas.model.B_pures(), [B_mat[i][i] for i in range(N)], rtol=1e-13)


    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas,
                                  B_model=VIRIAL_B_XIANG,
                                  cross_B_model=VIRIAL_B_XIANG,
                                  cross_B_model_kijs=kijs,
                                  C_model=VIRIAL_C_ZERO)
    gas = VirialGas(model=model, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz',
                T=T, P=P, zs=zs)

    # VIRIAL_B_XIANG
    B, dB, d2B, d3B = -4.558372700762532e-05, 3.8023980103500797e-07, -2.9686366447752638e-09, 3.4864810676267165e-11
    assert_close(gas.B(), B, rtol=1e-13)
    assert_close(gas.dB_dT(), dB, rtol=1e-13)
    assert_close(gas.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas.d3B_dT3(), d3B, rtol=1e-13)
    B_mat = gas.model.B_interactions()
    assert_close1d(gas.model.B_pures(), [B_mat[i][i] for i in range(N)], rtol=1e-13)


    # VIRIAL_B_MENG
    # Made up numbers to get a value of `a`
    dipoles = [[1e-5, 1e-3, 2e-2], [1e-3, 1e-6, 1e-2], [2e-2, 1e-2, 1e-8]]
    Tcijs = [[126.20000000000002, 151.21993365643428, 195.93376431845533], [151.21993365643428, 190.56399999999996, 230.38267751412258], [195.93376431845533, 230.38267751412258, 304.19999999999993]]
    Pcijs = [[3394387.499999999, 3851314.2142062606, 5005217.123514405], [3851314.2142062606, 4598999.999999997, 5573970.154312947], [5005217.123514405, 5573970.154312947, 7376459.999999995]]
    N = 3
    Meng_virial_as = [[Meng_virial_a(Tcijs[i][j], Pcijs[i][j], dipole=dipoles[i][j]) for j in range(N)]
                                     for i in range(N)]
    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas,
                                  B_model=VIRIAL_B_MENG,
                                  cross_B_model=VIRIAL_B_MENG,
                                  cross_B_model_kijs=kijs,
                                  B_model_Meng_as=Meng_virial_as,
                                  C_model=VIRIAL_C_ZERO)
    gas = VirialGas(model=model, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz',
                T=T, P=P, zs=zs)

    B, dB, d2B, d3B = (-4.4032087141036684e-05, 3.8809438374191875e-07, -3.195223834343181e-09, 3.857289293387547e-11)
    assert_close(gas.B(), B, rtol=1e-13)
    assert_close(gas.dB_dT(), dB, rtol=1e-13)
    assert_close(gas.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas.d3B_dT3(), d3B, rtol=1e-13)
    B_mat = gas.model.B_interactions()
    assert_close1d(gas.model.B_pures(), [B_mat[i][i] for i in range(N)], rtol=1e-13)

    # VIRIAL_B_TSONOPOULOS_EXTENDED
    # Made up parameters
    # These should always be symmetric
    # No real ideal how to use them except set the diagonal to real values and zero the rest
    BVirial_Tsonopoulos_extended_as = [[-0.0109, 0.0878, -6787.3],
     [0.0878, 0.5, 0.3],
     [-6787.3, .3, -5000.0]]

    BVirial_Tsonopoulos_extended_bs = [[1e-5, 1e-6, 1e-7], [1e-6, .25, 1e-8], [1e-7, 1e-8, .343]]


    model = VirialCSP(Tcs=Tcs, Pcs=Pcs, Vcs=Vcs, omegas=omegas,
                                  B_model=VIRIAL_B_TSONOPOULOS_EXTENDED,
                                  cross_B_model=VIRIAL_B_TSONOPOULOS_EXTENDED,
                                  cross_B_model_kijs=kijs,
                                 B_model_Tsonopoulos_extended_as=BVirial_Tsonopoulos_extended_as,
                                 B_model_Tsonopoulos_extended_bs=BVirial_Tsonopoulos_extended_bs,
                                  C_model=VIRIAL_C_ZERO)
    gas = VirialGas(model=model, HeatCapacityGases=HeatCapacityGases,
                    cross_B_model='theory', cross_C_model='Orentlicher-Prausnitz',
                T=T, P=1e4, zs=zs)

    B, dB, d2B, d3B = (-0.007155125156828212, 0.00014262531996979625, -3.3223750860211125e-06, 8.85625680727924e-08)
    assert_close(gas.B(), B, rtol=1e-13)
    assert_close(gas.dB_dT(), dB, rtol=1e-13)
    assert_close(gas.d2B_dT2(), d2B, rtol=1e-13)
    assert_close(gas.d3B_dT3(), d3B, rtol=1e-13)

    B_mat = gas.model.B_interactions()
    assert_close1d(gas.model.B_pures(), [B_mat[i][i] for i in range(N)], rtol=1e-13)
