示例#1
0
    def test_complete_sample(self):
        """
        trying to do a pretty complete one

        Note: This is more an integration test.  Each complex attribute of the
              PhysicalProperties dataclass should have its own pytests
        """
        p = PhysicalProperties()

        p.densities = DensityList([
            DensityPoint(density=Density(value=0.8751, unit="kg/m^3",
                                         standard_deviation=1.2,
                                         replicates=3),
                         ref_temp=Temperature(value=15.0, unit="C")),
            DensityPoint(density=Density(value=0.99, unit="kg/m^3",
                                         standard_deviation=1.4,
                                         replicates=5),
                         ref_temp=Temperature(value=25.0, unit="C"))
        ])

        py_json = p.py_json(sparse=False)  # the non-sparse version

        for name in ('densities',
                     'kinematic_viscosities',
                     'dynamic_viscosities'):
            assert name in py_json

        # Now test some real stuff:
        dens = py_json['densities']
        print(type(dens))

        assert type(dens) == list
        assert dens[0]['density']['value'] == 0.8751
    def test_converted_to(self):
        model = Temperature(value=273.15, unit='K')
        new = model.converted_to('C')

        assert model is not new

        assert model.value == 273.15
        assert model.unit == 'K'

        assert new.value == 0.0
        assert new.unit == 'C'
    def test_complete_sample(self):
        """
        trying to do a pretty complete one

        Note: This is more an integration test.  Each complex attribute of the
              Sample should have its own pytests
        """
        s = Sample(metadata=SampleMetaData(
            short_name="short", name="a longer name that is more descriptive"))
        p = PhysicalProperties()

        s.metadata.fraction_evaporated = MassFraction(value=11, unit='%')
        s.metadata.boiling_point_range = None

        p.densities = DensityList([
            DensityPoint(density=Density(value=0.8751,
                                         unit="kg/m^3",
                                         standard_deviation=1.2,
                                         replicates=3),
                         ref_temp=Temperature(value=15.0, unit="C")),
            DensityPoint(density=Density(value=0.99,
                                         unit="kg/m^3",
                                         standard_deviation=1.4,
                                         replicates=5),
                         ref_temp=Temperature(value=25.0, unit="C"))
        ])

        s.physical_properties = p

        py_json = s.py_json(sparse=False)  # the non-sparse version

        for name in ('CCME', 'ESTS_hydrocarbon_fractions', 'SARA',
                     'bulk_composition', 'compounds', 'cut_volume',
                     'distillation_data', 'environmental_behavior',
                     'extra_data', 'headspace_analysis', 'industry_properties',
                     'metadata', 'miscellaneous', 'physical_properties'):
            assert name in py_json

        assert py_json['metadata']['name'] == ('a longer name that is more '
                                               'descriptive')
        assert py_json['metadata']['short_name'] == "short"

        for name in ('densities', 'kinematic_viscosities',
                     'dynamic_viscosities'):
            assert name in py_json['physical_properties']

        # Now test some real stuff:
        dens = py_json['physical_properties']['densities']
        print(type(dens))

        assert type(dens) == list
        assert dens[0]['density']['value'] == 0.8751
示例#4
0
    def test_validate_no_duplicate_values(self):
        dp1 = DensityPoint(density=Density(value=900, unit='kg/m^3'),
                           ref_temp=Temperature(value=0, unit='C'),
                           )
        dp2 = DensityPoint(density=Density(value=900, unit='kg/m^3'),
                           ref_temp=Temperature(value=15, unit='C'),
                           )

        DL = DensityList((dp1, dp2))

        msgs = DL.validate()

        print(msgs)
        assert len(msgs) == 0
    def test_init_empty(self):
        model = Temperature()

        py_json = model.py_json()

        # should only have a unit_type
        assert py_json == {}

        # non-sparse should have all attributes present with None values
        py_json = model.py_json(sparse=False)

        assert py_json['unit_type'] == 'temperature'
        for attr in ('value', 'unit', 'min_value', 'max_value',
                     'standard_deviation', 'replicates'):
            assert py_json[attr] is None
示例#6
0
    def test_validate_non_numeric_value(self):
        dp1 = DensityPoint(density=Density(value=900, unit='kg/m^3'),
                           ref_temp=Temperature(value=0, unit='C'),
                           )
        dp2 = DensityPoint(density=Density(value="NM", unit='kg/m^3'),
                           ref_temp=Temperature(value=15, unit='C'),
                           )

        DL = DensityList((dp1, dp2))

        msgs = DL.validate()

        print(msgs)
        assert len(msgs) == 1
        assert "E044:" in msgs[0]
    def test_py_json(self):
        model = Temperature(value=273.15,
                            unit="K",
                            standard_deviation=1.2,
                            replicates=3)
        py_json = model.py_json()

        print(py_json)

        assert len(py_json) == 5
        assert py_json['value'] == 273.15
        assert py_json['unit'] == 'K'
        assert py_json['unit_type'] == 'temperature'

        assert py_json['standard_deviation'] == 1.2
        assert py_json['replicates'] == 3
def make_dist_cut_list(data, temp_unit='K'):

    cuts = [
        DistCut(fraction=Concentration(value=f, unit="fraction"),
                vapor_temp=Temperature(value=t, unit=temp_unit))
        for f, t in data
    ]
    return DistCutList(cuts)
    def test_patch_fix_C_K(self, monkeypatch, t, unit, result):

        temp_obj = Temperature(value=t, unit=unit)

        assert temp_obj.value == t

        print(f"{Temperature.fixCK=}")

        # turn on fix
        monkeypatch.setattr(Temperature, "fixCK", True)

        print(f"{Temperature.fixCK=}")

        temp_obj = Temperature(value=t, unit=unit)

        assert temp_obj.unit == 'C'
        assert temp_obj.value == result
示例#10
0
    def test_validate_bad_temp(self):
        dp1 = DensityPoint(density=Density(value=900, unit='kg/m^3'),
                           ref_temp=Temperature(value=0, unit='K'),
                           )
        dp2 = DensityPoint(density=Density(value=900, unit='kg/m^3'),
                           ref_temp=Temperature(value=20.0, unit='K'),
                           )

        DL = DensityList((dp1, dp2))

        msgs = DL.validate()

        print(msgs)

        assert len(msgs) == 2
        for msg in msgs:
            assert "E040:" in msg
            assert "Density" in msg
    def test_min_max(self):
        model = Temperature(min_value=273.15, max_value=288.15, unit="K")

        assert model.value is None
        assert model.min_value == 273.15
        assert model.max_value == 288.15
        assert model.unit == 'K'

        assert model.standard_deviation is None
        assert model.replicates is None
    def test_validation_no_fraction_recovered(self):
        dist = Distillation(type="mass fraction",
                            method="some arbitrary method",
                            end_point=Temperature(value=15, unit="C"),
                            fraction_recovered=None,
                            cuts=self.make_dist_cut_list(self.data,
                                                         temp_unit='C'))
        msgs = dist.validate()

        # make sure there is something there!
        assert len(msgs) == 1
        assert msgs[0].startswith('W009:')
示例#13
0
    def test_validate_negative_numeric_value(self):
        dp1 = DensityPoint(density=Density(value=900, unit='kg/m^3'),
                           ref_temp=Temperature(value=0, unit='C'),
                           )
        dp2 = DensityPoint(density=Density(value="0.0", unit='kg/m^3'),
                           ref_temp=Temperature(value=15, unit='C'),
                           )
        dp3 = DensityPoint(density=Density(value="-10.0", unit='kg/m^3'),
                           ref_temp=Temperature(value=15, unit='C'),
                           )

        DL = DensityList((dp1, dp2, dp3))

        msgs = DL.validate()

        print(msgs)
        assert len(msgs) == 2
        assert "E044:" in msgs[0]
        assert "0.0" in msgs[0]
        assert "E044:" in msgs[1]
        assert "-10.0" in msgs[1]
    def test_validation_bad_type(self):
        dist = Distillation(type="mass fractions",
                            method="some arbitrary method",
                            end_point=Temperature(value=15, unit="C"),
                            fraction_recovered=Concentration(value=0.8,
                                                             unit="fraction"),
                            cuts=self.make_dist_cut_list(self.data,
                                                         temp_unit='C'))
        msgs = dist.validate()

        # make sure there is something there!
        assert len(msgs) == 1
        assert "E032:" in msgs[0]
示例#15
0
def no_api_with_density():
    oil = Oil(oil_id='XXXXXX')

    oil.metadata.product_type = "Crude Oil NOS"
    print(oil)

    # create a sample for fresh oil
    s = Sample()

    # add some densities
    # p = PhysicalProperties()
    p = s.physical_properties
    p.densities = DensityList([
        DensityPoint(density=Density(value=0.8751, unit="g/cm^3"),
                     ref_temp=Temperature(value=60.0, unit="F")),
        DensityPoint(density=Density(value=0.99, unit="g/cm^3"),
                     ref_temp=Temperature(value=25.0, unit="C"))
    ])

    oil.sub_samples.append(s)

    return oil
    def test_validation_no_fraction_recovered_no_cuts(self):
        """
        if there is no cut data, it's OK not to have a fraction_recovered
        """
        dist = Distillation(
            type="mass fraction",
            method="some arbitrary method",
            end_point=Temperature(value=15, unit="C"),
            fraction_recovered=None,
        )
        msgs = dist.validate()

        # make sure there is something there!
        assert len(msgs) == 0
示例#17
0
    def test_comment_no_errors(self):
        """
        make sure we can add and save a comment to an InterfacialTensionPoint
        """
        itp = InterfacialTensionPoint(tension=None,
                                      ref_temp=Temperature(value=15.0,
                                                           unit="C"),
                                      comment="Too Viscous",
                                      )
        itl = InterfacialTensionList((itp,))

        msgs = itl.validate()

        assert not msgs
示例#18
0
    def test_missing_ref_temp(self):
        # this was in the actual data -- how?
        # note missing value for temp

        itp = InterfacialTensionPoint(tension=InterfacialTension(0.03,
                                                                 unit="N/m"),
                                      ref_temp=Temperature(unit="K"),
                                      )
        itl = InterfacialTensionList((itp,))

        msgs = itl.validate()

        assert "E042:" in msgs[0]
        assert "InterfacialTension" in msgs[0]
示例#19
0
    def test_validate_one_value(self):
        """
        it shouldn't crash (or give an warning) with one value!
        """
        dp1 = DensityPoint(density=Density(value=900, unit='kg/m^3'),
                           ref_temp=Temperature(value=0, unit='C'),
                           )

        DL = DensityList((dp1,))

        msgs = DL.validate()

        print(msgs)
        assert len(msgs) == 0
    def test_validation_bad_fraction(self):
        dist = Distillation(type="mass fraction",
                            method="some arbitrary method",
                            end_point=Temperature(value=15, unit="C"),
                            fraction_recovered=Concentration(value=0.8,
                                                             unit="fraction"),
                            cuts=self.make_dist_cut_list(self.data,
                                                         temp_unit='C'))
        dist.cuts[2].fraction.value = 1.1
        dist.cuts[4].fraction.value = -0.9

        msgs = dist.validate()

        errs = sum(1 for e in msgs if 'E041:' in e)
        assert errs == 2
    def test_std_dev_replicates(self):
        """
        Note on validation: If there is a standard deviation, there should be
                            more than 1 replicate and if there is more than
                            one replicate, there should probably be a
                            non-zero standard deviation
        """
        model = Temperature(standard_deviation=1.2, replicates=3)

        assert model.value is None
        assert model.min_value is None
        assert model.max_value is None
        assert model.unit is None

        assert model.standard_deviation == 1.2
        assert model.replicates == 3
    def test_validation_bad_temps(self):
        dist = Distillation(type="mass fraction",
                            method="some arbitrary method",
                            end_point=Temperature(value=15, unit="C"),
                            fraction_recovered=Concentration(value=0.8,
                                                             unit="fraction"),
                            cuts=self.make_dist_cut_list(self.data,
                                                         temp_unit='K'))
        msgs = dist.validate()

        print(msgs)
        # make sure there is something there!
        assert msgs

        errs = sum(1 for e in msgs if 'E040:' in e)
        assert errs == 7
    def test_from_py_json(self):
        model = Temperature.from_py_json({
            'value': 273.15,
            'unit': 'K',
            'standard_deviation': 1.2,
            'replicates': 3
        })

        assert model.value == 273.15
        assert model.min_value is None
        assert model.max_value is None
        assert model.unit == 'K'
        assert model.unit_type == 'temperature'

        assert model.standard_deviation == 1.2
        assert model.replicates == 3
    def test_distillation(self):
        dist = Distillation(type="mass fraction",
                            method="some arbitrary method",
                            end_point=Temperature(value=15, unit="C"),
                            fraction_recovered=Concentration(value=0.8,
                                                             unit="fraction"),
                            cuts=self.make_dist_cut_list(self.data,
                                                         temp_unit='C'))

        # few random things
        assert len(dist.cuts) == 11
        assert dist.cuts[3].fraction.value == 0.3
        assert dist.cuts[3].vapor_temp.value == 90.6

        assert dist.fraction_recovered == Concentration(0.8, unit="fraction")

        msgs = dist.validate()
        assert msgs == []
class TestExxonMapper():
    """
    This is where the real work happens!
    """
    # fixme -- there should probably be a fixture to get a record
    record = next(ExxonDataReader(example_index, example_dir).get_records())

    def test_init(self):
        with pytest.raises(TypeError):
            _mapper = ExxonMapper()

    def test_init_invalid(self):
        with pytest.raises(TypeError):
            _mapper = ExxonMapper(None)

    def test_header(self):
        oil = ExxonMapper(self.record)

        assert oil.metadata.name == 'HOOPS Blend'
        assert oil.metadata.reference.reference.startswith("ExxonMobil")
        assert oil.metadata.API == 35.2

    @pytest.mark.parametrize("index, expected", [
        (0, {
            'name': 'Fresh Oil Sample',
            'short_name': 'Fresh Oil',
        }),
        (1, {
            'name': 'Butane and Lighter IBP - 60F',
            'short_name': 'Butane and L...',
        }),
        (2, {
            'name': 'Lt. Naphtha C5 - 165F',
            'short_name': 'Lt. Naphtha ...',
        }),
        (3, {
            'name': 'Hvy Naphtha 165 - 330F',
            'short_name': 'Hvy Naphtha ...',
        }),
        (4, {
            'name': 'Kerosene 330 - 480F',
            'short_name': 'Kerosene 330...'
        }),
        (5, {
            'name': 'Diesel 480 - 650F',
            'short_name': 'Diesel 480 -...'
        }),
        (6, {
            'name': 'Vacuum Gas Oil 650 - 1000F',
            'short_name': 'Vacuum Gas O...'
        }),
        (7, {
            'name': 'Vacuum Residue 1000F+',
            'short_name': 'Vacuum Resid...'
        }),
    ])
    def test_sample_ids(self, index, expected):
        samples = ExxonMapper(self.record).sub_samples

        assert len(samples) == 8
        assert samples[index].metadata.name == expected['name']
        assert samples[index].metadata.short_name == expected['short_name']

    @pytest.mark.parametrize("index, expected", [
        (0, None),
        (1, Temperature(min_value=-57.641, max_value=60.0, unit='F')),
        (2, Temperature(min_value=60.0, max_value=165.0, unit='F')),
        (3, Temperature(min_value=165.0, max_value=330.0, unit='F')),
        (4, Temperature(min_value=330.0, max_value=480.0, unit='F')),
        (5, Temperature(min_value=480.0, max_value=650.0, unit='F')),
        (6, Temperature(min_value=650.0, max_value=1000.0, unit='F')),
        (7, Temperature(min_value=1000.0, max_value=1504.6, unit='F')),
    ])
    def test_boiling_point_range(self, index, expected):
        samples = ExxonMapper(self.record).sub_samples

        assert len(samples) == 8
        assert samples[index].metadata.boiling_point_range == expected

    @pytest.mark.parametrize("index, expected", [
        (0, VolumeFraction(100.0, unit="%")),
        (1, VolumeFraction(value=2.3128, unit='%')),
        (2, VolumeFraction(value=6.6891, unit='%')),
        (3, VolumeFraction(value=17.6059, unit='%')),
        (4, VolumeFraction(value=14.6657, unit='%')),
        (5, VolumeFraction(value=16.6362, unit='%')),
        (6, VolumeFraction(value=27.1985, unit='%')),
        (7, VolumeFraction(value=14.8918, unit='%')),
    ])
    def test_cut_volume(self, index, expected):
        samples = ExxonMapper(self.record).sub_samples

        assert samples[index].cut_volume == expected

    @pytest.mark.parametrize("sample_idx, density_idx, expected", [
        (0, 0, 0.84805),
        (7, 0, 0.99125),
    ])
    def test_densities(self, sample_idx, density_idx, expected):
        samples = ExxonMapper(self.record).sub_samples
        sample = samples[sample_idx]
        density = sample.physical_properties.densities[density_idx]

        assert isclose(density.density.value, expected, rel_tol=1e-3)

    @pytest.mark.parametrize("sample_idx, viscosity_idx, expected", [
        (0, 0, 6.739),
        (0, 2, 3.883),
        (3, 0, 0.89318),
        (3, 2, 0.64536),
    ])
    def test_kinematic_viscosities(self, sample_idx, viscosity_idx, expected):
        samples = ExxonMapper(self.record).sub_samples
        sample = samples[sample_idx]
        phys = sample.physical_properties
        viscosity = phys.kinematic_viscosities[viscosity_idx]

        # viscosity tests
        # whole oil
        assert viscosity.viscosity.value == expected
        assert viscosity.viscosity.unit == "cSt"

        for sample in samples:
            assert len(sample.physical_properties.dynamic_viscosities) == 0

    def test_dynamic_viscosities(self):
        samples = ExxonMapper(self.record).sub_samples

        for sample in samples:
            # no dynamic viscosities in the Exxon Assays
            assert len(sample.physical_properties.dynamic_viscosities) == 0

    @pytest.mark.parametrize("attr, indexes, values", [
        ('Sulfur Mass Fraction', range(8),
         (1.1494, 0.00019105, 0.0024387, 0.019791, 0.11793, 0.76024, 1.5844,
          3.047)),
        ('Naphthene Volume Fraction', range(8),
         (31.362, 0.0, 8.0856, 33.16, 40.822, 47.458, 35.381, 13.435)),
        ('Paraffin Volume Fraction', range(8),
         (35.538, 100.0, 91.914, 56.621, 42.772, 26.58, 18.992, 2.2913)),
        ('Nickel Mass Fraction', range(8),
         (8.183, None, None, None, None, None, None, 46.969)),
        ('Vanadium Mass Fraction', range(8),
         (17.215, None, None, None, None, None, None, 98.808)),
        ('Carbon Mass Fraction', range(8),
         (85.58, 82.43, 83.64, 85.43, 85.86, 86.06, 85.87, 85.43)),
        ('Hydrogen Mass Fraction', range(8),
         (13.26, 17.57, 16.36, 14.57, 14.04, 13.09, 12.36, 11.8)),
        ('Mercaptan Sulfur Mass Fraction', range(8),
         (0.5962, 0.7594, 8.106, 45.26, 32.83, 20.2, 3.227, 0.07651)),
        ('Nitrogen Mass Fraction', range(8),
         (968.12, 0.0, 0.0, 0.061811, 1.7976, 47.62, 782.36, 4176.0)),
        ('Calcium Mass Fraction', range(8),
         (5.9, None, None, None, None, None, None, None)),
        ('Hydrogen Sulfide Concentration', range(8),
         (0.0, None, None, None, None, None, None, None)),
        ('Salt Content', range(8),
         (0.0026, None, None, None, None, None, None, None)),
    ])
    def test_bulk_composition(self, attr, indexes, values):
        """
        Data points that are classified in bulk composition:
        - Sulphur
        - Naphthenes
        - Paraffins
        - Nickel
        - Vanadium
        - Carbon
        - Hydrogen
        - Mercaptan Sulfur
        - Nitrogen
        - Calcium
        - Hydrogen Sulfide
        - Salt content

        Notes:
        - These values are now kept in a list of compounds held by the
          bulk_composition attribute
        - Ideally, the name & groups of each compound would have the
          original field text from the datasheet.  This is not the case
          at present.
        """
        samples = ExxonMapper(self.record).sub_samples

        for i, val in zip(indexes, values):
            filter_list = [
                c for c in samples[i].bulk_composition if c.name == attr
            ]

            if val is None:
                assert len(filter_list) == 0
            else:
                assert len(filter_list) == 1

                compound = filter_list[0]

                assert isclose(compound.measurement.value,
                               values[i],
                               rel_tol=1e-4)

    @pytest.mark.parametrize("attr, indexes, values", [
        ('Total Acid Number', range(8),
         (0.90915, 8.294e-08, 4.8689e-05, 0.004045, 0.20694, 1.3179, 1.8496,
          0.6179)),
        ('Reid Vapor Pressure', range(8),
         (8.7651, None, None, None, None, None, None, None)),
        ('Aniline Point', range(8),
         (None, None, None, None, 140.679, 155.3736, 177.6938, None)),
        ('Cetane Index 1990 (D4737)', range(8),
         (None, None, None, 36.2293, 45.0055, 49.9756, None, None)),
        ('Cloud Point', range(8),
         (None, None, None, -106.574729, -62.385169, 4.0361, None, None)),
        ('Smoke Point', range(8),
         (None, None, None, 29.8541, 22.4655, 13.7602, None, None)),
        ('Conradson Carbon Residue', range(8),
         (3.1904, None, None, None, None, None, 0.36241, 17.695)),
        ('Freeze Point', range(8),
         (None, None, None, -99.2151, -54.7016, 14.6042, None, None)),
    ])
    def test_industry_properties(self, attr, indexes, values):
        """
        Data points that are classified in industry properties:
        - Total Acid Number (Neutralization Number)
        - Reid Vapor Pressure

        - Aniline Point
        - Cetane Index
        - Vanadium
        - Cloud Point
        - Smoke Point
        - Conradson Carbon Residue
        - Conradson Residuum (Vacuum Residue)
        - Gel Point (Freeze Point)

        Notes:
        - These values are kept in a list of attributes held by the
          industry_properties attribute
        - Ideally, the name & groups of each compound would have the
          original field text from the datasheet.  This is not the case
          at present.
        """
        samples = ExxonMapper(self.record).sub_samples

        for i, val in zip(indexes, values):
            filter_list = [
                c for c in samples[i].industry_properties if c.name == attr
            ]
            if val is None:
                assert len(filter_list) == 0
            else:
                assert len(filter_list) == 1

                compound = filter_list[0]

                assert isclose(compound.measurement.value,
                               values[i],
                               rel_tol=1e-4)

    @pytest.mark.parametrize("prop, unit, unit_type", [
        ('Total Acid Number', 'mg/g', 'massfraction'),
        ('Reid Vapor Pressure', 'psi', 'pressure'),
        ('Aniline Point', 'F', 'temperature'),
        ('Cetane Index 1990 (D4737)', None, 'unitless'),
        ('Cloud Point', 'F', 'temperature'),
        ('Smoke Point', 'mm', 'length'),
        ('Freeze Point', 'F', 'temperature'),
    ])
    def test_industry_properties_units(self, prop, unit, unit_type):
        """
        Data points that are classified in industry properties:
        - Total Acid Number (Neutralization Number)
        - Reid Vapor Pressure

        - Aniline Point
        - Cetane Index
        - Vanadium
        - Cloud Point
        - Smoke Point
        - Conradson Carbon Residue
        - Conradson Residuum (Vacuum Residue)
        - Gel Point (Freeze Point)
        """
        # just check the zeroth one:

        print("testing:", prop)

        for sample in ExxonMapper(self.record).sub_samples:
            print(sample.industry_properties)
            for p in sample.industry_properties:
                print(f"\n{p.name=}")
                print(f"{prop=}")
                if p.name == prop:
                    measurement = p.measurement
                    print("checking units of:", prop)
                    assert measurement.unit == unit
                    assert measurement.unit_type == unit_type
                    return
                continue
        assert False
        # assert False, f"property: {p.name} is missing"

    @pytest.mark.parametrize("attr, indexes, values", [
        ('saturates', range(8),
         [None, None, None, None, None, None, None, None]),
        ('aromatics', range(8),
         [None, None, None, None, None, None, None, None]),
        ('resins', range(8), [None, None, None, None, None, None, None, None]),
        ('asphaltenes', range(8),
         [None, None, None, None, None, None, None, None]),
    ])
    def test_sara(self, attr, indexes, values):
        """
        Test the sara attributes:
        - Aromatics
        - Asphaltenes

        Note: saturates and resins are not found in the Exxon Assays
        Note: We have decided that instead of C7 asphaltenes & aromatics
              going into SARA, we will put them into the bulk_composition
              list.
        """
        samples = ExxonMapper(self.record).sub_samples

        for i, val in zip(indexes, values):
            sara = samples[i].SARA

            if val is None:
                assert getattr(sara, attr) is None
            else:
                sara_attr = getattr(sara, attr)

                assert isclose(sara_attr.value, values[i], rel_tol=1e-4)

    def test_dist_cuts_units(self):
        for sample in ExxonMapper(self.record).sub_samples:
            for cut in sample.distillation_data.cuts:
                assert cut.vapor_temp.unit == "C"
                assert cut.fraction.unit == "%"

    def test_dist_type(self):
        for sample in ExxonMapper(self.record).sub_samples:
            assert sample.distillation_data.type == 'volume fraction'

    @pytest.mark.parametrize("samp_ind, cut_index, fraction, temp_f", [
        (0, 0, 0.0, -57.641),
        (0, 4, 30.0, 364.28),
        (5, 9, 80.0, 615.34),
        (7, 11, 95.0, 1435.99),
    ])
    def test_dist_cuts(self, samp_ind, cut_index, fraction, temp_f):
        samples = ExxonMapper(self.record).sub_samples

        cut = samples[samp_ind].distillation_data.cuts[cut_index]

        assert cut.fraction.value == fraction
        assert isclose(cut.vapor_temp.value,
                       sigfigs(uc.convert("F", "C", temp_f), 5),
                       rel_tol=1e-4)

    @pytest.mark.parametrize("sample_idx, expected", [
        (0, 1453.75),
        (1, None),
        (5, 649.13),
        (7, 1504.63),
    ])
    def test_dist_end_point(self, sample_idx, expected):
        samples = ExxonMapper(self.record).sub_samples

        if expected is None:
            assert samples[sample_idx].distillation_data.end_point is None
        else:
            expected_c = sigfigs(uc.convert("F", "C", expected), 5)
            end_point = samples[sample_idx].distillation_data.end_point

            assert isclose(end_point.value, expected_c, rel_tol=1e-4)
            assert end_point.unit == 'C'

    def test_no_cuts_in_butane(self):
        assert (ExxonMapper(
            self.record).sub_samples[1].distillation_data.cuts == [])

    def test_save_to_json(self):
        """
        Save an example .json file.  This is not so much a test, but a job
        to provide sample data that people can look at.
        """
        mapper = ExxonMapper(self.record)
        py_json = mapper.py_json()

        py_json['status'] = []

        filename = 'EX-Example-Record.json'
        file_path = os.path.sep.join(
            adios_db.__file__.split(os.path.sep)[:-3] + ['examples', filename])

        print(f'saving to: {file_path}')
        with open(file_path, 'w', encoding="utf-8") as fd:
            json.dump(py_json, fd, indent=4, sort_keys=True)
class TestTemperature:
    """
    Fixme: We really need to enforce that *some* value is passed in
           The model should fail if there is no value at all
    """
    def test_init_empty(self):
        model = Temperature()

        py_json = model.py_json()

        # should only have a unit_type
        assert py_json == {}

        # non-sparse should have all attributes present with None values
        py_json = model.py_json(sparse=False)

        assert py_json['unit_type'] == 'temperature'
        for attr in ('value', 'unit', 'min_value', 'max_value',
                     'standard_deviation', 'replicates'):
            assert py_json[attr] is None

    def test_std_dev_replicates(self):
        """
        Note on validation: If there is a standard deviation, there should be
                            more than 1 replicate and if there is more than
                            one replicate, there should probably be a
                            non-zero standard deviation
        """
        model = Temperature(standard_deviation=1.2, replicates=3)

        assert model.value is None
        assert model.min_value is None
        assert model.max_value is None
        assert model.unit is None

        assert model.standard_deviation == 1.2
        assert model.replicates == 3

    def test_value(self):
        model = Temperature(value=273.15, unit="K")

        assert model.value == 273.15
        assert model.min_value is None
        assert model.max_value is None
        assert model.unit == 'K'

        assert model.standard_deviation is None
        assert model.replicates is None

    def test_min_max(self):
        model = Temperature(min_value=273.15, max_value=288.15, unit="K")

        assert model.value is None
        assert model.min_value == 273.15
        assert model.max_value == 288.15
        assert model.unit == 'K'

        assert model.standard_deviation is None
        assert model.replicates is None

    def test_py_json(self):
        model = Temperature(value=273.15,
                            unit="K",
                            standard_deviation=1.2,
                            replicates=3)
        py_json = model.py_json()

        print(py_json)

        assert len(py_json) == 5
        assert py_json['value'] == 273.15
        assert py_json['unit'] == 'K'
        assert py_json['unit_type'] == 'temperature'

        assert py_json['standard_deviation'] == 1.2
        assert py_json['replicates'] == 3

    def test_from_py_json(self):
        model = Temperature.from_py_json({
            'value': 273.15,
            'unit': 'K',
            'standard_deviation': 1.2,
            'replicates': 3
        })

        assert model.value == 273.15
        assert model.min_value is None
        assert model.max_value is None
        assert model.unit == 'K'
        assert model.unit_type == 'temperature'

        assert model.standard_deviation == 1.2
        assert model.replicates == 3

    def test_convert_to(self):
        model = Temperature(value=273.15, unit='K')
        model.convert_to('C')

        assert model.value == 0.0
        assert model.unit == 'C'

    def test_converted_to(self):
        model = Temperature(value=273.15, unit='K')
        new = model.converted_to('C')

        assert model is not new

        assert model.value == 273.15
        assert model.unit == 'K'

        assert new.value == 0.0
        assert new.unit == 'C'

    @pytest.mark.parametrize("model", [
        Temperature(value=273, unit='K'),
        Temperature(value=15.15, unit='C'),
        Temperature(value=14.85, unit='C'),
        Temperature(value=-0.15, unit='C'),
        Temperature(value=-0.85, unit='C'),
    ])
    def test_validate_C_K_conversion_15(self, model):
        # model = Temperature(value=273, unit='K')

        msgs = model.validate()

        print(msgs)

        assert "W010:" in msgs[0]

    @pytest.mark.parametrize("temp_obj, result", [
        (Temperature(value=273, unit='K'), 0.0),
        (Temperature(value=15.15, unit='C'), 15.0),
        (Temperature(value=14.85, unit='C'), 15.0),
        (Temperature(value=-0.15, unit='C'), 0.0),
        (Temperature(value=-0.85, unit='C'), -1.0),
    ])
    def test_fix_C_K(self, temp_obj, result):
        """
        check if we can auto-fix the C-K conversion
        """
        temp_obj.fix_C_K()
        assert temp_obj.unit == 'C'
        assert temp_obj.value == result


#                  "name": "Butane and Lighter IBP - 60F",
# -                    "unit": "F",
# -                    "min_value": 151.46,
# -                    "max_value": 60.0,
# +                    "unit": "C",
# +                    "min_value": 66.36666666666667,
# +                    "max_value": 15.555555555555543,
#                      "unit_type": "temperature"
#                  }
#              },

    @pytest.mark.parametrize("temp_obj", [
        (Temperature(value=273, unit='F')),
        (Temperature(min_value=15.15, max_value=60.0, unit='F')),
        (Temperature(value=60.15, unit='F')),
        (Temperature(value=0.16, unit='C')),
        (Temperature(value=-0.86, unit='C')),
    ])
    def test_fix_C_K_no_change(self, temp_obj):
        """
        if temps are not in C or K, there should be no change.
        """
        t1 = temp_obj.copy()
        temp_obj.fix_C_K()
        assert temp_obj == t1

    @pytest.mark.parametrize("t, unit, result", [
        (273, 'K', 0.0),
        (15.15, 'C', 15.0),
        (14.85, 'C', 15.0),
        (-0.15, 'C', 0.0),
        (-0.85, 'C', -1.0),
    ])
    def test_patch_fix_C_K(self, monkeypatch, t, unit, result):

        temp_obj = Temperature(value=t, unit=unit)

        assert temp_obj.value == t

        print(f"{Temperature.fixCK=}")

        # turn on fix
        monkeypatch.setattr(Temperature, "fixCK", True)

        print(f"{Temperature.fixCK=}")

        temp_obj = Temperature(value=t, unit=unit)

        assert temp_obj.unit == 'C'
        assert temp_obj.value == result
    def test_convert_to(self):
        model = Temperature(value=273.15, unit='K')
        model.convert_to('C')

        assert model.value == 0.0
        assert model.unit == 'C'