Exemplo n.º 1
0
def test_df2str_nonstrict_monotonocity_valueerror(series, monotonocity):
    """Check we get ValueError in the correct circumstances"""
    with pytest.raises(ValueError):
        df2str(
            pd.DataFrame(data=series),
            digits=2,
            monotonocity=monotonocity,
        )
Exemplo n.º 2
0
    def SGOF(self, header: bool = True, dataincommentrow: bool = True) -> str:
        """
        Produce SGOF input for Eclipse reservoir simulator.

        The columns sg, krg, krog and pc are outputted and
        formatted accordingly.

        Meta-information for the tabulated data are printed
        as Eclipse comments.

        Args:
            header: Whether the SGOF string should be emitted.
                If you have multiple satnums, you should have True only
                for the first (or False for all, and emit the SGOF yourself).
                Defaults to True.
            dataincommentrow: Whether metadata should be printed,
                defaults to True.
        """
        if not self.fast and not self.selfcheck():
            # selfcheck() will log error/warning messages
            return ""
        string = ""
        if "PC" not in self.table:
            self.table["PC"] = 0.0
            self.pccomment = "-- Zero capillary pressure\n"
        if header:
            string += "SGOF\n"
        string += comment_formatter(self.tag)
        string += "-- pyscal: " + str(pyscal.__version__) + "\n"
        if dataincommentrow:
            string += self.sgcomment
            string += self.krgcomment
            string += self.krogcomment
            if not self.fast:
                string += "-- krg = krog @ sg=%1.5f\n" % self.crosspoint()
            string += self.pccomment
        width = 10
        string += ("-- " + "SG".ljust(width - 3) + "KRG".ljust(width) +
                   "KROG".ljust(width) + "PC".ljust(width) + "\n")
        string += df2str(
            self.table[["SG", "KRG", "KROG", "PC"]],
            monotonicity={
                "KROG": {
                    "sign": -1,
                    "lower": 0,
                    "upper": 1
                },
                "KRG": {
                    "sign": 1,
                    "lower": 0,
                    "upper": 1
                },
                "PC": {
                    "sign": 1,
                    "allowzero": True
                },
            } if not self.fast else None,
        )
        string += "/\n"
        return string
Exemplo n.º 3
0
    def SOF3(self, header=True, dataincommentrow=True):
        """Return a SOF3 string, combining data from the wateroil and
        gasoil objects.

        So - the oil saturation ranges from 0 to 1-swl. The saturation points
        from the WaterOil object is used to generate these
        """
        if self.wateroil is None or self.gasoil is None:
            logger.error("Both WaterOil and GasOil is needed for SOF3")
            return ""
        self.threephaseconsistency()

        # Copy of the wateroil data:
        table = pd.DataFrame(self.wateroil.table[["sw", "krow"]])
        table["so"] = 1 - table["sw"]

        # Copy of the gasoil data:
        gastable = pd.DataFrame(self.gasoil.table[["sg", "krog"]])
        gastable["so"] = 1 - gastable["sg"] - self.wateroil.swl

        # Merge WaterOil and GasOil on oil saturation, interpolate for
        # missing data (potentially different sg- and sw-grids)
        sof3table = (pd.concat(
            [table, gastable],
            sort=True).set_index("so").sort_index().interpolate(
                method="slinear").fillna(method="ffill").fillna(
                    method="bfill").reset_index())
        sof3table["soint"] = list(
            map(int, list(map(round, sof3table["so"] * SWINTEGERS))))
        sof3table.drop_duplicates("soint", inplace=True)

        # The 'so' column has been calculated from floating point numbers
        # and the zero value easily becomes a negative zero, circumvent this:
        zerorow = np.isclose(sof3table["so"], 0.0)
        sof3table.loc[zerorow, "so"] = abs(sof3table.loc[zerorow, "so"])

        string = ""
        if header:
            string += "SOF3\n"
        wo_tag = comment_formatter(self.wateroil.tag)
        go_tag = comment_formatter(self.gasoil.tag)
        if wo_tag != go_tag:
            string += wo_tag
            string += go_tag
        else:
            # Only print once if they are equal
            string += wo_tag
        string += "-- pyscal: " + str(pyscal.__version__) + "\n"
        if dataincommentrow:
            string += self.wateroil.swcomment
            string += self.gasoil.sgcomment
            string += self.wateroil.krowcomment
            string += self.gasoil.krogcomment

        width = 10
        string += ("-- " + "SW".ljust(width - 3) + "KROW".ljust(width) +
                   "KROG".ljust(width) + "\n")
        string += df2str(sof3table[["so", "krow", "krog"]])
        string += "/\n"
        return string
Exemplo n.º 4
0
def test_df2str_nonstrict_monotonicity_digits1(series, monotonicity, expected):
    """Test that we can have non-strict monotonicity at upper and/or lower limits"""
    assert (df2str(
        pd.DataFrame(data=series),
        digits=1,
        monotonicity=monotonicity,
    ).splitlines() == expected)
Exemplo n.º 5
0
    def SLGOF(self, header=True, dataincommentrow=True):
        """Produce SLGOF input for Eclipse reservoir simulator.

        The columns sl (liquid saturation), krg, krog and pc are
        outputted and formatted accordingly.

        Meta-information for the tabulated data are printed
        as Eclipse comments.

        Args:
            header: boolean for whether the SLGOF string should be emitted.
                If you have multiple satnums, you should have True only
                for the first (or False for all, and emit the SGOF yourself).
                Defaults to True.
            dataincommentrow: boolean for wheter metadata should be printed,
                defaults to True.
        """
        if not self.selfcheck():
            # Selfcheck will issue error messages.
            return ""
        string = ""
        if "pc" not in self.table:
            self.table["pc"] = 0.0
            self.pccomment = "-- Zero capillary pressure\n"
        if header:
            string += "SLGOF\n"
        string += comment_formatter(self.tag)
        string += "-- pyscal: " + str(pyscal.__version__) + "\n"
        if dataincommentrow:
            string += self.sgcomment
            string += self.krgcomment
            string += self.krogcomment
            string += "-- krg = krog @ sg=%1.5f\n" % self.crosspoint()
            string += self.pccomment
        width = 10
        string += ("-- " + "SL".ljust(width - 3) + "KRG".ljust(width) +
                   "KROG".ljust(width) + "PC".ljust(width) + "\n")
        string += df2str(
            self.slgof_df(),
            monotonocity={
                "krog": {
                    "sign": 1,
                    "lower": 0,
                    "upper": 1
                },
                "krg": {
                    "sign": -1,
                    "lower": 0,
                    "upper": 1
                },
                "pc": {
                    "sign": -1,
                    "allowzero": True
                },
            },
        )
        string += "/\n"
        return string
Exemplo n.º 6
0
    def SWOF(self, header=True, dataincommentrow=True):
        """
        Produce SWOF input for Eclipse reservoir simulator.

        The columns sw, krw, krow and pc are outputted and
        formatted accordingly.

        Meta-information for the tabulated data are printed
        as Eclipse comments.

        Args:
            header (bool): Indicate whether the SWOF string should
                be emitted. If you have multiple SATNUMs, you should
                set this to True only for the first (or False for all,
                and emit the SWOF yourself). Default True
            dataincommentrow (bool): Wheter metadata should
                be printed. Defualt True

        """
        if not self.fast and not self.selfcheck():
            # selfcheck failed and has issued an error message
            return ""
        string = ""
        if header:
            string += "SWOF\n"
        string += comment_formatter(self.tag)
        string += "-- pyscal: " + str(pyscal.__version__) + "\n"
        if "pc" not in self.table.columns:
            self.table["pc"] = 0.0
            self.pccomment = "-- Zero capillary pressure\n"
        if dataincommentrow:
            string += self.swcomment
            string += self.krwcomment
            string += self.krowcomment
            if not self.fast:
                string += "-- krw = krow @ sw=%1.5f\n" % self.crosspoint()
            string += self.pccomment
        width = 10
        string += (
            "-- "
            + "SW".ljust(width - 3)
            + "KRW".ljust(width)
            + "KROW".ljust(width)
            + "PC".ljust(width)
            + "\n"
        )
        string += df2str(
            self.table[["sw", "krw", "krow", "pc"]],
            monotonocity={
                "krow": {"sign": -1, "lower": 0, "upper": 1},
                "krw": {"sign": 1, "lower": 0, "upper": 1},
                "pc": {"sign": -1, "allowzero": True},
            }
            if not self.fast
            else None,
        )
        string += "/\n"  # Empty line at the end
        return string
Exemplo n.º 7
0
    def GOTABLE(self,
                header: bool = True,
                dataincommentrow: bool = True) -> str:
        """
        Produce GOTABLE input for the Nexus reservoir simulator.

        The columns sg, krg, krog and pc are outputted and
        formatted accordingly.

        Meta-information for the tabulated data are printed
        as Eclipse comments.

        Args:
            header: boolean for whether the SGOF string should be emitted.
                If you have multiple satnums, you should have True only
                for the first (or False for all, and emit the SGOF yourself).
                Defaults to True.
            dataincommentrow: boolean for wheter metadata should be printed,
                defaults to True.
        """
        string = ""
        if "PC" not in self.table.columns:
            self.table["PC"] = 0.0
            self.pccomment = "-- Zero capillary pressure\n"
        if header:
            string += "GOTABLE\n"
            string += "SG KRG KROG PC\n"
        string += "! pyscal: " + str(pyscal.__version__) + "\n"
        if dataincommentrow:
            string += self.sgcomment.replace("--", "!")
            string += self.krgcomment.replace("--", "!")
            string += self.krogcomment.replace("--", "!")
            string += "! krg = krog @ sw=%1.5f\n" % self.crosspoint()
            string += self.pccomment.replace("--", "!")
        width = 10
        string += ("! " + "SG".ljust(width - 2) + "KRG".ljust(width) +
                   "KROG".ljust(width) + "PC".ljust(width) + "\n")
        string += df2str(
            self.table[["SG", "KRG", "KROG", "PC"]],
            monotonicity={
                "KROG": {
                    "sign": -1,
                    "lower": 0,
                    "upper": 1
                },
                "KRG": {
                    "sign": 1,
                    "lower": 0,
                    "upper": 1
                },
                "PC": {
                    "sign": 1,
                    "allowzero": True
                },
            } if self.fast else None,
        )
        return string
Exemplo n.º 8
0
def test_df2str_monotone():
    """Test the monotonicity enforcement in df2str()

    This test function essentially tests the function
    utils/monotonicity.py::modify_dframe_monotonicity
    """

    # A constant nonzero column, makes no sense as capillary pressure
    # but still we ensure it runs in eclipse:
    assert (df2str(pd.DataFrame(data=[1, 1, 1]),
                   digits=2,
                   monotonicity={0: {
                       "sign": -1
                   }}) == "1.00\n0.99\n0.98\n")
    assert (df2str(pd.DataFrame(data=[1, 1, 1]),
                   digits=2,
                   monotonicity={0: {
                       "sign": -1
                   }}) == "1.00\n0.99\n0.98\n")
    assert (df2str(pd.DataFrame(data=[1, 1, 1]),
                   digits=2,
                   monotonicity={0: {
                       "sign": -1
                   }}) == "1.00\n0.99\n0.98\n")
    assert (df2str(pd.DataFrame(data=[1, 1, 1]),
                   digits=2,
                   monotonicity={0: {
                       "sign": 1
                   }}) == "1.00\n1.01\n1.02\n")
    assert (df2str(pd.DataFrame(data=[1, 1, 1]),
                   digits=2,
                   monotonicity={0: {
                       "sign": 1
                   }}) == "1.00\n1.01\n1.02\n")
    assert (df2str(pd.DataFrame(data=[1, 1, 1]),
                   digits=7,
                   monotonicity={0: {
                       "sign": -1
                   }}) == "1.0000000\n0.9999999\n0.9999998\n")

    # For strict monotonicity we will introduce negativity:
    dframe = pd.DataFrame(data=[0.00001, 0.0, 0.0, 0.0], columns=["PC"])
    assert (df2str(dframe, monotonicity={"PC": {
        "sign": -1
    }}) == "0.0000100\n0.0000000\n-0.0000001\n-0.0000002\n")

    # Actual data that has occured:
    dframe = pd.DataFrame(
        data=[0.0000027, 0.0000026, 0.0000024, 0.0000024, 0.0000017],
        columns=["PC"])
    assert (df2str(dframe, monotonicity={"PC": {
        "sign": -1
    }}) == "0.0000027\n0.0000026\n0.0000024\n0.0000023\n0.0000017\n")
Exemplo n.º 9
0
    def WOTABLE(self, header=True, dataincommentrow=True):
        """Return a string for a Nexus WOTABLE"""
        string = ""
        if "pc" not in self.table.columns:
            self.table["pc"] = 0.0
            self.pccomment = "-- Zero capillary pressure\n"

        if header:
            string += "WOTABLE\n"
            string += "SW KRW KROW PC\n"
        string += "! pyscal: " + str(pyscal.__version__) + "\n"
        if dataincommentrow:
            string += self.swcomment.replace("--", "!")
            string += self.krwcomment.replace("--", "!")
            string += self.krowcomment.replace("--", "!")
            if not self.fast:
                string += "! krw = krow @ sw=%1.5f\n" % self.crosspoint()
            string += self.pccomment.replace("--", "!")
        width = 10
        string += (
            "! "
            + "SW".ljust(width - 2)
            + "KRW".ljust(width)
            + "KROW".ljust(width)
            + "PC".ljust(width)
            + "\n"
        )
        string += df2str(
            self.table[["sw", "krw", "krow", "pc"]],
            monotonocity={
                "krow": {"sign": -1, "lower": 0, "upper": 1},
                "krw": {"sign": 1, "lower": 0, "upper": 1},
                "pc": {"sign": -1, "allowzero": True},
            }
            if not self.fast
            else None,
        )
        return string
Exemplo n.º 10
0
    def SGFN(
        self,
        header: bool = True,
        dataincommentrow: bool = True,
        sgcomment: Optional[str] = None,
        crosspointcomment: Optional[str] = None,
    ):
        """
        Produce SGFN input for Eclipse reservoir simulator.

        The columns sg, krg, and pc are outputted and
        formatted accordingly.

        Meta-information for the tabulated data are printed
        as Eclipse comments.

        Args:
            header: boolean for whether the SGFN string should be emitted.
                If you have multiple satnums, you should have True only
                for the first (or False for all, and emit the SGFN yourself).
                Defaults to True.
            dataincommentrow: boolean for wheter metadata should be printed,
                defaults to True.
            sgcomment: Provide the string to include in the comment
                section for describing the saturation endpoints. Used
                by GasWater.
            crosspointcomment: String to be used for crosspoint comment
                string, overrides what this object can provide. Used by GasWater.
                If None, it will be computed, use empty string to avoid.
        """
        if not self.selfcheck(mode="SGFN"):
            # Selfcheck will issue error messages.
            return ""
        string = ""
        if "PC" not in self.table.columns:
            self.table["PC"] = 0.0
            self.pccomment = "-- Zero capillary pressure\n"
        if header:
            string += "SGFN\n"
        string += comment_formatter(self.tag)
        string += "-- pyscal: " + str(pyscal.__version__) + "\n"
        if dataincommentrow:
            if sgcomment is not None:
                string += sgcomment
            else:
                string += self.sgcomment
            string += self.krgcomment
            if crosspointcomment is None:
                if "KROG" in self.table.columns:
                    string += "-- krg = krog @ sg=%1.5f\n" % self.crosspoint()
            else:
                string += crosspointcomment
            string += self.pccomment
        width = 10
        string += ("-- " + "SG".ljust(width - 3) + "KRG".ljust(width) +
                   "PC".ljust(width) + "\n")
        string += df2str(
            self.table[["SG", "KRG", "PC"]],
            monotonicity={
                "KRG": {
                    "sign": 1,
                    "lower": 0,
                    "upper": 1
                },
                "PC": {
                    "sign": 1,
                    "allowzero": True
                },
            } if not self.fast else None,
        )
        string += "/\n"
        return string
Exemplo n.º 11
0
def test_df2str():
    """Test handling of roundoff issues when printing dataframes

    See also test_gasoil.py::test_roundoff()
    """
    # Easy cases:
    assert df2str(pd.DataFrame(data=[0.1]), digits=1).strip() == "0.1"
    assert df2str(pd.DataFrame(data=[0.1]), digits=3).strip() == "0.100"
    assert df2str(pd.DataFrame(data=[0.1]), digits=3,
                  roundlevel=3).strip() == "0.100"
    assert df2str(pd.DataFrame(data=[0.1]), digits=3,
                  roundlevel=4).strip() == "0.100"
    assert df2str(pd.DataFrame(data=[0.1]), digits=3,
                  roundlevel=5).strip() == "0.100"
    assert df2str(pd.DataFrame(data=[0.01]), digits=3,
                  roundlevel=2).strip() == "0.010"

    # Here roundlevel will ruin the result:
    assert df2str(pd.DataFrame(data=[0.01]), digits=3,
                  roundlevel=1).strip() == "0.000"

    # Tricky ones:
    # This one should be rounded down:
    assert df2str(pd.DataFrame(data=[0.0034999]), digits=3).strip() == "0.003"
    # But if we are on the 9999 side due to representation error, the
    # number probably represents 0.0035 so it should be rounded up
    assert (df2str(pd.DataFrame(data=[0.003499999999998]),
                   digits=3,
                   roundlevel=5).strip() == "0.004")
    # If we round to more digits than we have in IEE754, we end up truncating:
    assert (df2str(
        pd.DataFrame(data=[0.003499999999998]), digits=3,
        roundlevel=20).strip() == "0.003"  # "Wrong" due to IEE754 truncation.
            )
    # If we round straight to out output, we are not getting the chance to correct for
    # the representation error:
    assert (df2str(pd.DataFrame(data=[0.003499999999998]),
                   digits=3,
                   roundlevel=3).strip() == "0.003"  # Wrong
            )
    # So roundlevel > digits
    assert (df2str(pd.DataFrame(data=[0.003499999999998]),
                   digits=3,
                   roundlevel=4).strip() == "0.004")
    # But digits < roundlevel < 15 works:
    assert (df2str(pd.DataFrame(data=[0.003499999999998]),
                   digits=3,
                   roundlevel=14).strip() == "0.004")
    assert (df2str(pd.DataFrame(data=[0.003499999999998]),
                   digits=3,
                   roundlevel=15).strip() == "0.003"  # Wrong
            )

    # Double rounding is a potential issue, as:
    assert round(0.0034445, 5) == 0.00344
    assert round(round(0.0034445, 6), 5) == 0.00345  # Wrong
    # So if pd.to_csv would later round instead of truncate, we could be victim
    # of this, having roundlevel >  digits + 1 would avoid that:
    assert round(round(0.0034445, 7), 5) == 0.00344
Exemplo n.º 12
0
def test_df2str_monotone():
    """Test the monotonocity enforcement in df2str()"""

    # A constant nonzero column, makes no sense as capillary pressure
    # but still we ensure it runs in eclipse:
    assert (
        df2str(pd.DataFrame(data=[1, 1, 1]), digits=2, monotone_column=0)
        == "1.00\n0.99\n0.98\n"
    )
    assert (
        df2str(
            pd.DataFrame(data=[1, 1, 1]),
            digits=2,
            monotone_column=0,
            monotone_direction=-1,
        )
        == "1.00\n0.99\n0.98\n"
    )
    assert (
        df2str(
            pd.DataFrame(data=[1, 1, 1]),
            digits=2,
            monotone_column=0,
            monotone_direction="dec",
        )
        == "1.00\n0.99\n0.98\n"
    )
    assert (
        df2str(
            pd.DataFrame(data=[1, 1, 1]),
            digits=2,
            monotone_column=0,
            monotone_direction=1,
        )
        == "1.00\n1.01\n1.02\n"
    )
    assert (
        df2str(
            pd.DataFrame(data=[1, 1, 1]),
            digits=2,
            monotone_column=0,
            monotone_direction="inc",
        )
        == "1.00\n1.01\n1.02\n"
    )
    assert (
        df2str(pd.DataFrame(data=[1, 1, 1]), digits=7, monotone_column=0)
        == "1.0000000\n0.9999999\n0.9999998\n"
    )

    # For strict monotonicity we will introduce negativity:
    dframe = pd.DataFrame(data=[0.00001, 0.0, 0.0, 0.0], columns=["pc"])
    assert (
        df2str(dframe, monotone_column="pc")
        == "0.0000100\n0.0000000\n-0.0000001\n-0.0000002\n"
    )

    # Actual data that has occured:
    dframe = pd.DataFrame(
        data=[0.0000027, 0.0000026, 0.0000024, 0.0000024, 0.0000017], columns=["pc"]
    )
    assert (
        df2str(dframe, monotone_column="pc")
        == "0.0000027\n0.0000026\n0.0000024\n0.0000023\n0.0000017\n"
    )
Exemplo n.º 13
0
    def SWFN(
        self, header=True, dataincommentrow=True, swcomment=None, crosspointcomment=None
    ):
        """Return a SWFN keyword with data to Eclipse

        The columns sw, krw and pc are outputted and formatted
        accordingly.

        Meta-information for the tabulated data are printed
        as Eclipse comments.

        Args:
            header: boolean for whether the SWFN string should be emitted.
                If you have multiple satnums, you should have True only
                for the first (or False for all, and emit the SWFN yourself).
                Defaults to True.
            dataincommentrow: boolean for wheter metadata should be printed,
                defaults to True.
            swcomment (str): String to be used for swcomment, overrides what
                this object can provide. Used by GasWater
            crosspointcomment (str): String to be used for crosspoint comment
                string, overrides what this object can provide. Used by GasWater.
                If None, it will be computed, use empty string to avoid.
        """
        if not self.selfcheck(mode="SWFN"):
            # selfcheck will print errors/warnings
            return ""
        string = ""
        if "pc" not in self.table.columns:
            self.table["pc"] = 0.0
            self.pccomment = "-- Zero capillary pressure\n"
        if header:
            string += "SWFN\n"
        string += comment_formatter(self.tag)
        string += "-- pyscal: " + str(pyscal.__version__) + "\n"
        if dataincommentrow:
            if swcomment is not None:
                string += swcomment
            else:
                string += self.swcomment
            string += self.krwcomment
            if crosspointcomment is None:
                if "krow" in self.table.columns and not self.fast:
                    string += "-- krw = krow @ sw=%1.5f\n" % self.crosspoint()
            else:
                string += crosspointcomment
            string += self.pccomment
        width = 10
        string += (
            "-- "
            + "SW".ljust(width - 3)
            + "KRW".ljust(width)
            + "PC".ljust(width)
            + "\n"
        )
        string += df2str(
            self.table[["sw", "krw", "pc"]],
            monotonocity={
                "krw": {"sign": 1, "lower": 0, "upper": 1},
                "pc": {"sign": -1, "allowzero": True},
            }
            if not self.fast
            else None,
        )
        string += "/\n"  # Empty line at the end
        return string