def test_swu_equal_swatinit(simulator):
    """Test SWU equal to SWATINIT, this is the same as SWATINIT_1

    Eclipse will ignore SWATINIT because it is equal to SWU.
    """
    model = PillarModel(
        cells=1,
        apex=900,  # pc is around 1.443238 here.
        owc=[1000],
        swatinit=[0.9],
        swu=[0.9],  # Behaviour in Eclipse is discontinuous at swu=swatinit
        maxpc=[3.0],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    swat_from_pc_input = model.evaluate_sw(1.443238)
    assert np.isclose(swat_from_pc_input, 0.51513567)
    if "flow" in simulator:
        assert np.isclose(qc_frame["SWAT"][0], 0.9)
        assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
    else:
        assert np.isclose(qc_frame["SWAT"][0], swat_from_pc_input)
        # There is no scaling when SWATINIT==SWU:
        assert np.isclose(qc_frame["PC_SCALING"][0], 1)
        assert qc_frame["QC_FLAG"][0] == __SWATINIT_1__
    print(qc_frame)
def test_swatinit_almost1_slightly_above_contact(simulator, tmp_path):
    """The result is discontinuous close to swatinit=1 for Eclipse100, because
    at swatinit = 1 - epsilon, Eclipse will try to scale  the capillary
    pressure, and is only limited by PPCWMAX (but not in this test).

    flow is not discontinuous in SWAT as a function of SWATINIT, but in PPCW as a
    function of SWATINIT.
    """
    os.chdir(tmp_path)

    if "flow" in simulator:
        p_cap = 0.37392
    else:
        p_cap = 0.3738366

    model = PillarModel(cells=1, apex=1000, owc=[1030], swatinit=[0.999], swl=[0.1])
    qc_frame = run_reservoir_simulator(simulator, model)
    assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
    assert np.isclose(qc_frame["SWAT"][0], 0.999)
    assert np.isclose(qc_frame["PC"], p_cap, atol=0.001)
    needed_scaling = p_cap / model.evaluate_pc(0.999)
    # Worryingly inaccurate?
    assert np.isclose(qc_frame["PPCW"][0], needed_scaling * 3.0, atol=1)

    qc_vols = qc_volumes(qc_frame)
    assert np.isclose(qc_vols[__PC_SCALED__], 0.0, atol=0.0003)
def test_swu_lessthan_swatinit(simulator):
    """Test SWU equal to SWATINIT

    In Eclipse this looks like the same situation as SWATINIT_1,
    SWATINIT is totally ignored. In flow, it looks like the SWU
    value (which here is the SWOF table endpoint) is ignored
    """
    model = PillarModel(
        cells=1,
        apex=900,  # pc is around 1.443238 here.
        owc=[1000],
        swatinit=[0.9],
        swu=[0.8],
        maxpc=[3.0],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    swat_from_pc_input = model.evaluate_sw(1.443238)
    assert np.isclose(swat_from_pc_input, 0.463244)
    if "flow" in simulator:
        assert np.isclose(qc_frame["SWAT"][0], 0.9)
        assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
        # Flow does not scale the PC:
        assert np.isclose(qc_frame["PC_SCALING"], 1.0)
        assert np.isclose(qc_frame["PPCW"], 3.0)
    else:
        assert np.isclose(qc_frame["SWAT"][0], swat_from_pc_input)
        # There is no scaling when SWU < SWATINIT:
        assert np.isclose(qc_frame["PC_SCALING"][0], 1)
        assert qc_frame["QC_FLAG"][0] == __SWATINIT_1__
    print(qc_frame)
def test_swatinit_1_far_above_contact(simulator, tmp_path):
    """If SWATINIT is 1 far above the contact, we are in an unstable
    situation (water should not be mobile e.g)

    Eclipse doc says this:
    "If a cell is given saturation corresponding to a zero capillary pressure
    (typically 1.0) above the contact, then the Pc curve cannot be scaled to
    honor the saturation, hence the Pc curve is left unscaled."

    The SWATINIT is effectively ignored by Eclipse100: SWAT is taken from
    the SWOF table and the relevant Pc pressure, and since that Pc curve
    is not touched by SWATINIT, SWAT becomes SWL far above contact.
    """
    os.chdir(tmp_path)
    model = PillarModel(
        cells=1, apex=1000, owc=[2000], swatinit=[1], swl=[0.1], maxpc=[3.0]
    )

    qc_frame = run_reservoir_simulator(simulator, model)

    qc_vols = qc_volumes(qc_frame)

    assert qc_frame["QC_FLAG"][0] == __SWATINIT_1__
    if "flow" in simulator:
        # Flow accepts this swatinit, but this water will flow out
        assert np.isclose(qc_frame["SWAT"][0], 1)
        # PPCW is the input Pc:
        assert np.isclose(qc_frame["PPCW"][0], 3.0)

        assert np.isclose(qc_vols[__SWATINIT_1__], (1 - 1) * qc_frame["PORV"])
    else:
        # E100 ignores SWATINIT and sets the saturation to SWL:
        assert np.isclose(qc_frame["SWAT"][0], 0.1)
        assert np.isclose(qc_frame["PPCW"][0], 3.0)
        # Negative number means water is lost:
        assert np.isclose(qc_vols[__SWATINIT_1__], -(1 - 0.1) * qc_frame["PORV"])
    # Not possible to compute PC, it should be Nan:
    assert np.isnan(qc_frame["PC"][0])

    # Bigger reservoir model, so that OWC is within the grid, should
    # not make a difference:
    biggermodel = PillarModel(
        cells=200, apex=1000, owc=[2000], swatinit=[1] * 200, swl=[0.1]
    )
    qc_frame = run_reservoir_simulator(simulator, biggermodel)
    assert set(qc_frame["QC_FLAG"]) == set([__SWATINIT_1__, __WATER__])
    assert qc_frame[qc_frame["Z"] < 2000]["QC_FLAG"].unique()[0] == __SWATINIT_1__
    assert qc_frame[qc_frame["Z"] > 2000]["QC_FLAG"].unique()[0] == __WATER__
    if "flow" in simulator:
        assert np.isclose(qc_frame["SWAT"][0], 1)
        # PPCW is the input Pc:
        assert np.isclose(qc_frame["PPCW"][0], 3.0)
    else:
        # E100 ignores SWATINIT and sets the saturation to SWL:
        assert np.isclose(qc_frame["SWAT"][0], 0.1)
        assert np.isclose(qc_frame["PPCW"][0], 3.0)
    assert np.isnan(qc_frame["PC"][0])
def test_swat_limited_by_ppcwmax_above_contact(simulator, tmp_path):
    """Test PPCWMAX far above contact. This keyword is only supported by Eclipse100
    and will be ignored by flow.

    This leads to water being lost from SWATINIT to SWAT.
    """
    os.chdir(tmp_path)
    swatinit = 0.8
    model = PillarModel(
        cells=1, apex=1000, owc=[1100], swatinit=[swatinit], ppcwmax=[3.01]
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    assert np.isclose(qc_frame["PPCWMAX"][0], 3.01)
    qc_vols = qc_volumes(qc_frame)
    swat_if_ppcwmax = 0.5746147
    if "eclipse" in simulator:
        # for PPCWMAX set to 3.01 Eclipse100 will scale the swatinit value to this:
        assert qc_frame["QC_FLAG"][0] == __PPCWMAX__
        assert np.isclose(qc_frame["SWAT"][0], swat_if_ppcwmax)
        assert np.isclose(qc_frame["PC_SCALING"][0], 3.01 / 3.00)
        actual_pc = 1.4226775
        assert np.isclose(
            model.evaluate_pc(swat_if_ppcwmax, scaling=3.01 / 3.00), actual_pc
        )
        assert np.isclose(qc_frame["PC"][0], actual_pc)

        assert np.isclose(
            qc_vols[__PPCWMAX__], (swat_if_ppcwmax - swatinit) * qc_frame["PORV"][0]
        )
    else:
        # "flow" does not support PPCWMAX and will scale the Pc curve as much is needed
        # and will thus reproduce swatinit:
        assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
        assert np.isclose(qc_frame["SWAT"][0], swatinit)
        expected_scaling = 2.107745
        assert np.isclose(qc_frame["PC_SCALING"][0], expected_scaling)

        actual_pc = 1.405163
        # The capillary pressure in this cell:
        assert np.isclose(
            model.evaluate_pc(swatinit, scaling=expected_scaling), actual_pc
        )
        assert np.isclose(qc_frame["PC"][0], actual_pc)
        # If we had done scaling according to PPCWMAX, we would have gotten
        # the same result as in Eclipse:
        assert np.isclose(
            # Worryingly inaccurate?
            model.evaluate_sw(actual_pc, scaling=3.01 / 3.00),
            swat_if_ppcwmax,
            atol=0.02,
            # (opm probably uses monotone cubic interpolation in SWOF, but that
            # should not affect SWOF tables with only two points)
        )

        # Not very accurate:
        assert np.isclose(qc_vols[__PC_SCALED__], 0, atol=0.001)
def test_swatinit_less_than_1_below_contact_neg_pc(simulator, tmpdir):
    """For an oil-wet system, there can be oil below free water level.

    Flow will set water saturation to 1 no questions asked. Bug?

    Eclipse ignores SWATINIT but calculates SWAT based on the input
    Pc-curve, and can thus give SWAT<1 if pc_min < 0.
    """
    tmpdir.chdir()
    model = PillarModel(
        cells=1,
        apex=1000,
        owc=[900],
        swatinit=[0.7],
        swl=[0.1],
        maxpc=[3.0],
        minpc=[-3.0],
    )
    # Eclipse will pick this SWAT:
    expected_swat = 0.7915066

    # This must then be the Pc in the cell:
    actual_pc = -1.610044
    p_cap = model.evaluate_pc(expected_swat)
    assert np.isclose(p_cap, actual_pc)
    qc_frame = run_reservoir_simulator(simulator, model)
    assert qc_frame["QC_FLAG"][0] == __HC_BELOW_FWL__

    qc_vols = qc_volumes(qc_frame)
    if "flow" in simulator:
        assert np.isclose(qc_frame["SWAT"][0], 1.0)
        assert np.isclose(qc_frame["PPCW"][0], 3.0)
        assert np.isclose(qc_frame["PC_SCALING"][0], 1.0)

        # Computed Pc is wrong here, but is what corresponds
        # to the saturation picked by OPM-flow:
        assert np.isclose(qc_frame["PC"][0], -3.0)

        assert np.isclose(qc_vols[__HC_BELOW_FWL__],
                          (1 - 0.7) * qc_frame["PORV"][0])
    else:
        assert np.isclose(qc_frame["SWAT"][0], expected_swat)
        # PPCW is set to NaN, so we don't have that column
        if "PPCW" in qc_frame:
            assert pd.isnull(qc_frame["PPCW"][0])
        if "PC_SCALING" in qc_frame:
            assert pd.isnull(qc_frame["PC_SCALING"][0])
        if "PC" in qc_frame:
            assert pd.isnull(qc_frame["PC"][0])
        assert np.isclose(qc_frame["PCW"][0], 3.0)  # Untouched input

        assert np.isclose(
            qc_vols[__HC_BELOW_FWL__],
            (expected_swat - 0.7) * qc_frame["PORV"][0],
            atol=0.1,
        )
def test_below_capillary_entry_pressure(simulator, tmp_path):
    """Test what we get below the capillary entry pressure"""
    os.chdir(tmp_path)

    if "flow" in simulator:
        pc_10m_above_contact = 0.150006
    else:
        pc_10m_above_contact = 0.148862

    model = PillarModel(
        cells=1,
        apex=1015,  # The cell is then between 1015 and 1025 m, cell centre 1020
        owc=[1030],
        swatinit=[1],
        swl=[0.1],
        oip_init=0,
        minpc=[pc_10m_above_contact],
        maxpc=[3],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    assert np.isclose(qc_frame["SWAT"][0], 1)
    assert np.isclose(qc_frame["PPCW"][0], 3.0)
    assert np.isclose(qc_frame["PC"][0], pc_10m_above_contact)
    assert np.isclose(qc_frame["PC_SCALING"][0], 1.0)

    assert qc_frame["QC_FLAG"][0] == __SWATINIT_1__
def test_capillary_entry_pressure(simulator, tmp_path):
    """With some capillary entry pressure, we should have SWATINIT=1 some
    distance above the contact and also SWAT=1 there. Above the capillary entry
    pressure, both swat and swatinit should be less than 1."""
    os.chdir(tmp_path)

    if "flow" in simulator:
        pc_25m_above_contact = 0.373919
    else:
        pc_25m_above_contact = 0.373836

    model = PillarModel(
        cells=1,
        apex=1000,  # cell extends from 1000 to 1010 m, cell centre 1005
        owc=[1030],
        swatinit=[1],
        swl=[0.1],
        oip_init=0,
        minpc=[pc_25m_above_contact],
        maxpc=[3],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    assert np.isclose(qc_frame["SWAT"][0], 1)
    assert np.isclose(qc_frame["PPCW"][0], 3)
    assert np.isclose(qc_frame["PC_SCALING"][0], 1)
    assert np.isclose(qc_frame["PC"][0], pc_25m_above_contact)

    # Might not be important whether this is flagged as SWATINIT_1 or
    # PC_SCALED, as the volume difference is zero.
    assert qc_frame["QC_FLAG"][0] == __SWATINIT_1__
def test_swlpc_scaling(simulator, tmp_path):
    """Test that PC is scaled differently when SWLPC is included"""
    os.chdir(tmp_path)
    model = PillarModel(
        cells=1,
        apex=1000,
        owc=[1020],
        swatinit=[0.5],
        swl=[0.0],
        swlpc=[0.4],
        maxpc=[1.0],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    print(qc_frame)
    assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
    if "flow" in simulator:
        # SWLPC is partly supported by flow, the SWLPC data affects PC:
        # assert np.isclose(qc_frame["PC"], 0.224777)  # without SWLPC

        # check_swatinit's PC estimate assumes the SWLPC is in use, and this
        # perturbs the PC estimate, it should have been 0.224777:
        assert np.isclose(qc_frame["PC"], 0.374628)
        assert np.isclose(qc_frame["PC_SCALING"], 0.449553)
    else:
        assert np.isclose(qc_frame["PC"], 0.224276)  # this is independent of SWLPC
        # Without SWLPC, pc_scaling is 0.448552, but when SWLPC=0.4
        # the curve is pushed to the right before it is scaled vertically, and
        # then it must be pushed further down:
        assert np.isclose(qc_frame["PC_SCALING"], 0.269131)
def test_swat_higher_than_swatinit_via_swl_above_contact(simulator, tmp_path):
    """If SWL is set higher than SWATINIT, both Eclipse and flow
    truncates SWAT to SWL.

    QC category is "Water gained"
    """
    os.chdir(tmp_path)
    model = PillarModel(cells=1, apex=1000, owc=[2000], swatinit=[0.3], swl=[0.5])
    qc_frame = run_reservoir_simulator(simulator, model)
    assert qc_frame["QC_FLAG"][0] == __SWL_TRUNC__
    assert np.isclose(qc_frame["SWAT"][0], 0.5)
    assert np.isclose(qc_frame["SWATINIT"][0], 0.3)

    qc_vols = qc_volumes(qc_frame)
    assert np.isclose(qc_vols[__SWL_TRUNC__], (0.5 - 0.3) * qc_frame["PORV"][0])

    if "flow" in simulator:
        expected_ppcw = 12.650095
        assert np.isclose(qc_frame["PPCW"][0], expected_ppcw)  # oip_init=0
    else:
        assert np.isclose(qc_frame["PPCW"][0], 3.0)

    # When SWL is truncated, we cannot trust PC_SCALING to be used to
    # compute PC, so it is removed from the dataframe.
    assert pd.isnull(qc_frame["PC_SCALING"][0])
    assert pd.isnull(qc_frame["PC"][0])
def test_ppcwmax_gridvector(simulator, tmpdir):
    """Test that ppcwmax_gridvector maps ppcwmax values correctly in the grid"""
    tmpdir.chdir()
    model = PillarModel(
        cells=3,
        owc=[1050],
        satnum=[2, 1, 2],
        maxpc=[0.001, 0.002],
        ppcwmax=[0.01, 0.02],
    )
    # NB: Eclipse errors if PPCWMAX is smaller than maxpc pr. SATNUM. Flow does not.
    qc_frame = run_reservoir_simulator(simulator, model)
    assert np.isclose(qc_frame[qc_frame["SATNUM"] == 1]["PPCWMAX"].unique(),
                      0.01)
    assert np.isclose(qc_frame[qc_frame["SATNUM"] == 2]["PPCWMAX"].unique(),
                      0.02)
    if "flow" in simulator:
        # This will fail when/if flow implements PPCWMAX support
        assert np.isclose(qc_frame["PPCW"].sort_values(),
                          [0.558839, 0.782229, 1.006258]).all()
    else:
        assert np.isclose(qc_frame[qc_frame["SATNUM"] == 1]["PPCW"].unique(),
                          0.01)
        assert np.isclose(qc_frame[qc_frame["SATNUM"] == 2]["PPCW"].unique(),
                          0.02)
def test_rptrst_allprops(simulator, tmp_path, mocker):
    """Test what happens when RPTRST is ALLPROPS (which probably implies BASIC=1)"""
    os.chdir(tmp_path)
    model = PillarModel(rptrst="ALLPROPS")
    run_reservoir_simulator(simulator, model, perform_qc=False)
    mocker.patch("sys.argv", ["check_swatinit", "FOO.DATA"])
    main()  # No exceptions.
def test_swatinit_less_than_1_below_contact(simulator, tmp_path):
    """SWATINIT below the contact is ignored, and SWAT is set based on the
    input SWOF table. In water-wet system (pc>0), this always yields SWAT=1
    """
    os.chdir(tmp_path)
    model = PillarModel(cells=1, apex=1000, owc=[900], swatinit=[0.7], swl=[0.1])
    qc_frame = run_reservoir_simulator(simulator, model)
    qc_vols = qc_volumes(qc_frame)
    assert qc_frame["QC_FLAG"][0] == __HC_BELOW_FWL__
    assert np.isclose(qc_frame["SWAT"][0], 1)

    assert np.isclose(qc_vols[__HC_BELOW_FWL__], (1 - 0.7) * qc_frame["PORV"][0])
    if "flow" in simulator:
        assert np.isclose(qc_frame["PPCW"][0], 3.0)
        assert np.isclose(qc_frame["PC_SCALING"][0], 1.0)
        assert np.isclose(qc_frame["PC"], 0)
    else:
        # E100 will not report a PPCW in this case, libecl gives -1e20,
        # which becomes a NaN through ecl2df and then NaN columns are dropped.
        if "PPCW" in qc_frame:
            assert pd.isnull(qc_frame["PPCW"][0])
        if "PC_SCALING" in qc_frame:
            assert pd.isnull(qc_frame["PC_SCALING"][0])
        if "PC" in qc_frame:
            assert pd.isnull(qc_frame["PC"][0])
def test_rptrst_basic_1(simulator, tmp_path, mocker):
    """Test what happens when RPTRST is BASIC=1"""
    os.chdir(tmp_path)
    model = PillarModel(rptrst="BASIC=1")
    run_reservoir_simulator(simulator, model, perform_qc=False)
    mocker.patch("sys.argv", ["check_swatinit", "FOO.DATA"])
    main()  # No exceptions/errors.
def test_swu(simulator, tmp_path):
    """Test SWATINIT < SWU < 1.

    Both flow and Eclipse will scale the PC curve according to
    SWU and SWATINIT."""
    os.chdir(tmp_path)
    model = PillarModel(
        cells=1,
        apex=900,  # pc is around 1.443238 here.
        owc=[1000],
        swatinit=[0.9],
        swu=[0.95],
        maxpc=[3.0],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    # All good when SWU > SWATINIT
    assert np.isclose(qc_frame["SWAT"][0], 0.9)
    if "flow" in simulator:
        assert np.isclose(qc_frame["PC"][0], 1.442738)
        assert np.isclose(qc_frame["PC_SCALING"], 8.175515)
    else:
        # These PC values are the same for all SWU between SWATINIT and 1
        assert np.isclose(qc_frame["PC"][0], 1.443238)
        # But PPCW goes to infinity as SWU approaches SWATINIT
        assert np.isclose(qc_frame["PC_SCALING"], 8.178345)
    assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
def test_no_rptrst(tmpdir, mocker):
    """Test what happens when RPTRST is not included, no UNRST"""
    tmpdir.chdir()
    model = PillarModel(rptrst="")
    run_reservoir_simulator(find_flow_simulator(), model, perform_qc=False)
    mocker.patch("sys.argv", ["check_swatinit", "FOO.DATA"])
    with pytest.raises(SystemExit, match="UNRST"):
        main()
def test_accepted_swatinit_far_above_contact(simulator, tmpdir):
    """Test a "normal" scenario, SWATINIT is accepted and some PC scaling will be applied
    far above the contact
    """
    tmpdir.chdir()
    model = PillarModel(cells=1,
                        apex=1000,
                        owc=[1100],
                        swatinit=[0.1],
                        swl=[0.0],
                        maxpc=[3.0])
    qc_frame = run_reservoir_simulator(simulator, model)
    assert np.isclose(qc_frame["SWAT"][0], 0.1)
    assert qc_frame["QC_FLAG"][0] == __PC_SCALED__

    qc_vols = qc_volumes(qc_frame)
    assert np.isclose(qc_vols[__PC_SCALED__], 0, atol=0.0001)

    if "flow" in simulator:
        # Flow returns the unscaled SWOF input here
        assert np.isclose(qc_frame["PCW"][0], 3.0)
        expected_ppcw = 1.5612928
        assert np.isclose(
            qc_frame["PPCW"][0], expected_ppcw
        )  # This is what it had to be scaled to to reach swatinit.
        assert np.isclose(qc_frame["PC_SCALING"][0], expected_ppcw / 3.0)

        # The actual Pc value can be back-calculated from SWOF:
        actual_pc = model.evaluate_pc(0.1, scaling=expected_ppcw / 3.0)
        assert np.isclose(actual_pc, 1.4051635)  # in bars.
        assert np.isclose(qc_frame["PC"], actual_pc)
        # At surface conditions, density difference is 200 kg/m3, this number
        # is sort of "close" to 200 kg/m3 * 9.81 m/s^2 * 100 meters / 1e5 = 1.96
        # (mismatch due to Bo and compressibility)
    else:
        # Eclipse100, numbers are only slightly different:
        expected_ppcw = 1.5807527
        assert np.isclose(qc_frame["PCW"][0], expected_ppcw)
        assert np.isclose(qc_frame["PPCW"][0], expected_ppcw)
        assert np.isclose(qc_frame["PC_SCALING"][0], expected_ppcw / 3.0)
        # (cell centre is 5 meters above contact)

        # The actual Pc value can be back-calculated from SWOF:
        actual_pc = model.evaluate_pc(0.1, scaling=expected_ppcw / 3.0)
        assert np.isclose(actual_pc, 1.42267743)
        assert np.isclose(qc_frame["PC"], actual_pc)
def test_no_unifout(tmp_path, mocker):
    """Test what happens when UNIFOUT is not included"""
    os.chdir(tmp_path)
    model = PillarModel(unifout="")
    run_reservoir_simulator(find_flow_simulator(), model, perform_qc=False)
    mocker.patch("sys.argv", ["check_swatinit", "FOO.DATA"])
    with pytest.raises(SystemExit, match="UNIFOUT"):
        main()
def test_no_swatinit(tmp_path, mocker, caplog):
    """Test what check_swatinit does on a case not initialized by SWATINIT"""
    os.chdir(tmp_path)
    model = PillarModel(swatinit=[None])
    run_reservoir_simulator(find_flow_simulator(), model, perform_qc=False)
    mocker.patch("sys.argv", ["check_swatinit", "FOO.DATA"])
    main()
    assert "INIT-file/deck does not have SWATINIT" in caplog.text
def test_no_unrst(tmp_path, mocker):
    """Test what happens when there is no restart file with SWAT[0]"""
    os.chdir(tmp_path)
    model = PillarModel()
    run_reservoir_simulator(find_flow_simulator(), model, perform_qc=False)
    os.unlink("FOO.UNRST")
    mocker.patch("sys.argv", ["check_swatinit", "FOO.DATA"])
    with pytest.raises(SystemExit, match="UNRST"):
        main()
def test_no_filleps(tmp_path, mocker, caplog):
    """Test the output when we don't have SWL (FILLEPS is needed)"""
    os.chdir(tmp_path)
    model = PillarModel(filleps="")
    run_reservoir_simulator(find_flow_simulator(), model, perform_qc=False)
    mocker.patch("sys.argv", ["check_swatinit", "FOO.DATA"])
    main()
    assert "SWL not found" in caplog.text
    assert "FILLEPS" in caplog.text
def test_accepted_swatinit_slightly_above_contact(simulator, tmpdir):
    """Test a "normal" scenario, SWATINIT is accepted and some PC scaling will be applied
    some meters above the contact

    QC-wise, these cells will not be flagged, but contribute to average PC_SCALING
    """
    tmpdir.chdir()
    model = PillarModel(cells=1,
                        apex=1000,
                        owc=[1020],
                        swatinit=[0.5],
                        swl=[0.0],
                        maxpc=[3.0])
    qc_frame = run_reservoir_simulator(simulator, model)
    # E100 is not very accurate here, flow gives exactly 0.5
    assert np.isclose(qc_frame["SWAT"][0], 0.5, atol=0.001)
    assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
    # Here, the Pc-curve is scaled (it goes from 3 to 0 in SWOF). At
    # swatinit=0.5, pc_swof is 1.5.

    qc_vols = qc_volumes(qc_frame)
    assert np.isclose(qc_vols[__PC_SCALED__], 0, atol=0.00001)

    if "flow" in simulator:
        # Flow returns the unscaled SWOF input here
        assert np.isclose(qc_frame["PCW"][0], 3.0)

        expected_ppcw = 0.4495535

        assert np.isclose(qc_frame["PPCW"][0], expected_ppcw)

        # This is what it had to be scaled to to reach swatinit.
        assert np.isclose(qc_frame["PC_SCALING"][0], expected_ppcw / 3.0)

        actual_pc = model.evaluate_pc(0.5, scaling=expected_ppcw / 3.0)
        assert np.isclose(actual_pc, 0.22477675)
        assert np.isclose(qc_frame["PC"], actual_pc)
    else:
        # Eclipse100, numbers are a tad different:
        assert np.isclose(qc_frame["PCW"][0], 0.4485523)
        assert np.isclose(qc_frame["PPCW"][0], 0.4485523)
def test_ppcwmax_gridvector_eqlnum(simulator, tmp_path):
    """Test that ppcwmax unrolling also works with EQLNUM (historical bug)"""
    os.chdir(tmp_path)
    model = PillarModel(
        cells=3,
        satnum=[2, 1, 2],
        eqlnum=[3, 2, 1],
        owc=[1051, 1052, 1053],
        maxpc=[0.001, 0.002],
        ppcwmax=[0.01, 0.02],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    assert qc_frame[qc_frame["SATNUM"] == 1]["PPCWMAX"].unique() == [0.01]
    assert qc_frame[qc_frame["SATNUM"] == 2]["PPCWMAX"].unique() == [0.02]
def test_accepted_swatinit_in_gas(simulator, tmp_path):
    """Repeat the test above, but with a gas-oil contact below the reservoir cell

    This gives higher capillary pressure (larger scaling) in the single reservoir cell.
    """
    os.chdir(tmp_path)
    model = PillarModel(
        cells=1,
        phases=["OIL", "WATER", "GAS"],
        apex=1000,
        owc=[1100],
        goc=[1050],
        swatinit=[0.1],
        swl=[0.0],
        maxpc=[3.0],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    assert np.isclose(qc_frame["SWAT"][0], 0.1)
    assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
    # Capillary pressure number are the same as when goc is not used:
    if "flow" in simulator:
        # Flow returns the unscaled SWOF input here
        assert np.isclose(qc_frame["PCW"][0], 3.0)
        expected_ppcw = 5.773265
        assert np.isclose(qc_frame["PPCW"][0], expected_ppcw)
        assert np.isclose(qc_frame["PC_SCALING"][0], expected_ppcw / 3.0)
        actual_pc = model.evaluate_pc(0.1, scaling=expected_ppcw / 3.0)
        assert np.isclose(actual_pc, 5.1959385)  # in bars.
        assert np.isclose(qc_frame["PC"], actual_pc)
    else:
        # Eclipse100, numbers are slightly different:
        expected_ppcw = 5.79644012
        assert np.isclose(qc_frame["PCW"][0], expected_ppcw)
        assert np.isclose(qc_frame["PPCW"][0], expected_ppcw)
        actual_pc = model.evaluate_pc(0.1, scaling=expected_ppcw / 3.0)
        assert np.isclose(actual_pc, 5.216796)  # in bars.
        assert np.isclose(qc_frame["PC"], actual_pc)
def test_pc_scaled_above_gwc(simulator, tmp_path):
    """Test a two-phase gas-water problem, scaled capillary pressure above contact"""

    os.chdir(tmp_path)
    model = PillarModel(
        cells=1,
        phases=["WATER", "GAS"],
        apex=1000,
        owc=[1100],
        swatinit=[0.5],
        swl=[0.1],
        maxpc=[3.0],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
    assert np.isclose(qc_frame["PPCW"][0], 16.913918)
    assert np.isclose(qc_frame["PC"][0], 9.396621)
def test_swlpc_trunc(simulator, tmp_path):
    """SWAT truncated by SWLPC is the same as being truncated by SWL"""
    os.chdir(tmp_path)
    model = PillarModel(
        cells=1,
        apex=1000,
        owc=[1020],
        swatinit=[0.1],
        swl=[0.0],
        swlpc=[0.8],
        maxpc=[3.0],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    print(qc_frame)
    if "eclipse" in simulator:
        assert qc_frame["QC_FLAG"][0] == __SWL_TRUNC__
    else:
        # SWLPC is not supported by flow, the SWLPC data is effectively ignored.
        assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
def test_swlpc_correcting_swl(simulator, tmp_path):
    """SWLPC should be allowed to override SWL, so that
    if an SWL value would trigger SWL_TRUNC, we can save the day by SWLPC"""
    os.chdir(tmp_path)
    model = PillarModel(
        cells=1,
        apex=1000,
        owc=[1020],
        swatinit=[0.1],
        swl=[0.4],
        swlpc=[0.0],
        maxpc=[3.0],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    print(qc_frame)
    if "eclipse" in simulator:
        assert qc_frame["QC_FLAG"][0] == __PC_SCALED__
    else:
        # SWLPC is not supported by flow, the SWLPC data is effectively ignored.
        assert qc_frame["QC_FLAG"][0] == __SWL_TRUNC__
def test_swatinit_1_slightly_above_contact(simulator, tmpdir):
    """If we are slightly above the contact, item 9 in EQUIL plays
    a small role.

    SWATINIT=1 is still ignored above contact, Pc curve is left untouched.
    """
    tmpdir.chdir()
    model = PillarModel(cells=1,
                        apex=1000,
                        owc=[1030],
                        swatinit=[1],
                        swl=[0.1],
                        oip_init=0)
    qc_frame = run_reservoir_simulator(simulator, model)
    assert qc_frame["QC_FLAG"][0] == __SWATINIT_1__
    qc_vols = qc_volumes(qc_frame)
    if "flow" in simulator:
        expected_swat = 0.887824
        actual_pc = 0.37392
    else:
        expected_swat = 0.887849
        actual_pc = 0.3738366

    if "flow" in simulator:
        # Flow accepts this swatinit, but this water will flow out.
        assert np.isclose(qc_frame["SWAT"][0], 1)
        assert np.isnan(qc_frame["PC"][0])
        assert np.isclose(qc_vols[__SWATINIT_1__], (1 - 1) * qc_frame["PORV"])
    else:
        # E100:
        assert np.isclose(qc_frame["SWAT"][0], expected_swat)
        assert np.isclose(qc_frame["PC"][0], actual_pc)
        assert np.isclose(qc_vols[__SWATINIT_1__],
                          (expected_swat - 1) * qc_frame["PORV"])
    assert model.evaluate_pc(0.1) == 3.0
    assert model.evaluate_pc(1) == 0

    # The actual capillary pressure in this cell:
    assert np.isclose(model.evaluate_pc(expected_swat), actual_pc)

    # Check that if we run without SWATINIT, even flow will give this
    # saturation:
    model.swatinit = [None]  # hacking the model object
    qc_frame = run_reservoir_simulator(simulator, model)
    assert np.isclose(qc_frame["SWAT"][0], expected_swat, atol=0.001)
def test_swatinit_1_below_contact(simulator, tmp_path):
    """An all-good scenario, below contact, water-wet, ask for water, we get water."""
    os.chdir(tmp_path)
    model = PillarModel(
        cells=1,
        apex=1000,
        owc=[100],
        swatinit=[1],
        swl=[0.1],
        maxpc=[3.0],
    )
    qc_frame = run_reservoir_simulator(simulator, model)
    assert qc_frame["QC_FLAG"][0] == __WATER__
    assert np.isclose(qc_frame["SWAT"][0], 1)
    if "flow" in simulator:
        assert np.isclose(qc_frame["PPCW"][0], 3.0)
        assert np.isclose(qc_frame["PC"][0], 0)
    else:
        if "PPCW" in qc_frame:
            assert pd.isnull(qc_frame["PPCW"][0])

    qc_vols = qc_volumes(qc_frame)
    assert np.isclose(qc_vols[__WATER__], 0.0)