def test_check_empty_array(self) -> None: assert Array.FromScalars(scalars=[]) == Array.CreateEmptyArray() assert Array.FromScalars(scalars=[], unit="m") == Array([], "m") expected_msg = "If category and value are given, the unit must be specified too." with pytest.raises(AssertionError, match=expected_msg): Array.FromScalars(scalars=[], category="length")
def test_convert_description_to_alfacase_with_empty_dict( datadir: Path) -> None: """ Ensure that the conversion from a description into a Alfacase it's not generating empty dict. Since it's not a valid syntax for strictyaml resulting in an InconsistentIndentationDisallowed error """ simple_case = case_description.CaseDescription( name="Simple Case", pipes=[ case_description.PipeDescription( name="pipe", source="mass_source_inlet", target="pressure_outlet", segments=build_simple_segment(), profile=case_description.ProfileDescription( x_and_y=case_description.XAndYDescription( x=Array([0], "m"), y=Array([0], "m"))), ) ], ) simple_case_alfacase_content = convert_description_to_alfacase(simple_case) assert "wall_description: {}" not in simple_case_alfacase_content assert "tables: {}" not in simple_case_alfacase_content # Smoke check, ensures that the alfacase is loaded correctly without errors simple_case_alfacase_file = datadir / "simple_case.alfacase" simple_case_alfacase_file.write_text(data=simple_case_alfacase_content, encoding="UTF-8") loaded_alfacase = DescriptionDocument.from_file(simple_case_alfacase_file) assert loaded_alfacase.content["name"].data == simple_case.name
def testArrayOperations(unit_database_len_time) -> None: unit_database = unit_database_len_time m = Quantity.CreateDerived(OrderedDict([("Table size", ["m", 1])])) km_city = Quantity.CreateDerived(OrderedDict([("City size", ["km", 1])])) s1: Array[List[float]] = Array.CreateWithQuantity(m, [1]) s2: Array[List[float]] = Array.CreateWithQuantity(km_city, [0.01]) initial1 = s1.GetQuantity().GetComposingUnits() initial2 = s2.GetQuantity().GetComposingUnits() # Check that they doesn't raise ComposedUnitError s1.GetValues() s2.GetValues() quantity, value = unit_database.Multiply(m, km_city, 1, 0.01) assert initial1 == s1.GetQuantity().GetComposingUnits() assert initial2 == s2.GetQuantity().GetComposingUnits() calculated1: Array[List[float]] = Array.CreateWithQuantity(quantity, [value]) array = s1 * s2 str(array) # just to see if it works... assert calculated1 == s1 * s2 quantity, value = unit_database.Sum(m, km_city, 1, 0.01) assert Array.CreateWithQuantity(quantity, [value]) == s1 + s2 quantity, value = unit_database.Subtract(m, km_city, 1, 0.01) assert Array.CreateWithQuantity(quantity, [value]) == s1 - s2
def testCopyPropertiesAndValidation(unit_database_len) -> None: # Not raises exception because by default validation is False on the copy operation array_source = Array("flow rate", values=[-1, -2, -3], unit="m3/s") assert not array_source.IsValid() array_dest = array_source.CreateCopy() assert array_dest.values == [-1, -2, -3]
def default_well() -> case_description.WellDescription: """ Minimum valid WellDescription """ return case_description.WellDescription( name="Well 1", profile=case_description.ProfileDescription( length_and_elevation=case_description.LengthAndElevationDescription( length=Array([0.0] + [1000] * 2, "m"), elevation=Array([0.0] + [1.2] * 2, "m"), ) ), stagnant_fluid="Lift Gas", top_node="Node 1", bottom_node="Node 2", annulus=case_description.AnnulusDescription( has_annulus_flow=False, top_node="Node 1" ), formation=case_description.FormationDescription( reference_y_coordinate=Scalar(0, "m") ), environment=case_description.EnvironmentDescription( thermal_model=constants.PipeThermalModelType.SteadyState ), )
def build_compressor_pressure_table_description( speed_entries, corrected_mass_flow_rate_entries, pressure_ratio_table, isentropic_efficiency_table, ): """ Helper to build a table for CompressorPressureTable from a parametrized input of `speed_entries` and `corrected_mass_flow_rate_entries`, correcting it so len(pressure_ratio_table) and len(isentropic_efficiency_table) have elements = len(speed_entries) * len(corrected_mass_flow_rate_entries), making it a explicit table with all combinations. """ expected_size = len(speed_entries) * len(corrected_mass_flow_rate_entries) assert expected_size == len( pressure_ratio_table ), f"Missing pressure entries. Expected {expected_size} but got {len(pressure_ratio_table)}" assert expected_size == len( isentropic_efficiency_table ), f"Missing isentropic efficiency entries. Expected {expected_size} but got {len(isentropic_efficiency_table)}" adjusted_speeds = [] adjusted_flow_rates = [] for speed in speed_entries: for flow_rate in corrected_mass_flow_rate_entries: adjusted_speeds.append(speed) adjusted_flow_rates.append(flow_rate) return case_description.CompressorPressureTableDescription( speed_entries=Array(adjusted_speeds, speed_entries.unit), corrected_mass_flow_rate_entries=Array( adjusted_flow_rates, corrected_mass_flow_rate_entries.unit), pressure_ratio_table=pressure_ratio_table, isentropic_efficiency_table=isentropic_efficiency_table, )
def test_cv_table_description(): expected_msg = "Opening and Flow Coefficient must have the same size, got 2 items for flow_coefficient and 1 for opening" with pytest.raises(ValueError, match=re.escape(expected_msg)): case_description.CvTableDescription( opening=Array([0.0], "-"), flow_coefficient=Array([0.0, 2], "(galUS/min)/(psi^0.5)"), )
def build_constant_initial_tracers_mass_fractions_description(values, unit): return case_description.InitialTracersMassFractionsDescription( position_input_type=constants.TableInputType.length, table_length=case_description.TracersMassFractionsContainerDescription( positions=Array([0.0], "m"), tracers_mass_fractions=[Array(values, unit)]), )
def testNumberOverArray() -> None: a = Array([2.0, 2.0], "m") b = Array([3.0, 3.0], "m") c = 1.0 / a assert approx(c.GetValues("1/m")) == [0.5, 0.5] assert approx((3.0 / a).GetValues("1/m")) == [1.5, 1.5] assert b / a == b * 1 / a == b * (1 / a)
def testDivision(unit_database_len_time): unit_database = unit_database_len_time m = Quantity.CreateDerived(odict([("Table size", ["m", 1])])) km_city = Quantity.CreateDerived(odict([("City size", ["km", 1])])) quantity, value = unit_database.Divide(m, km_city, 1, 0.01) calculated1 = Array.CreateWithQuantity(quantity, [value]) s1 = Array.CreateWithQuantity(m, [1]) s2 = Array.CreateWithQuantity(km_city, [0.01]) assert calculated1 == s1 / s2
def test_build_compressor_pressure_table_description_invalid( pressure_ratios, isentropic_efficiencies, error_msg): with pytest.raises(AssertionError, match=error_msg): build_compressor_pressure_table_description( speed_entries=Array([0, 1000], "rpm"), corrected_mass_flow_rate_entries=Array([1, 2, 3], "kg/s"), pressure_ratio_table=Array(pressure_ratios, "-"), isentropic_efficiency_table=Array(isentropic_efficiencies, "-"), )
def testDivision(unit_database_len_time) -> None: unit_database = unit_database_len_time m = Quantity.CreateDerived(OrderedDict([("Table size", ["m", 1])])) km_city = Quantity.CreateDerived(OrderedDict([("City size", ["km", 1])])) quantity, value = unit_database.Divide(m, km_city, 1, 0.01) calculated1: Array[List[float]] = Array.CreateWithQuantity(quantity, [value]) s1: Array[List[float]] = Array.CreateWithQuantity(m, [1]) s2: Array[List[float]] = Array.CreateWithQuantity(km_city, [0.01]) assert calculated1 == s1 / s2
def build_constant_initial_volume_fractions_description(values): return case_description.InitialVolumeFractionsDescription( position_input_type=constants.TableInputType.length, table_length=case_description.VolumeFractionsContainerDescription( positions=Array([0.0], "m"), fractions={k: Array([v.value], v.unit) for k, v in values.items()}, ), )
def testNumpyConversion(unit_database_custom_conversion): unit_database = unit_database_custom_conversion from barril.units.posc import MakeBaseToCustomary, MakeCustomaryToBase f_unit_to_base = MakeCustomaryToBase(273.15, 1, 1, 0) f_base_to_unit = MakeBaseToCustomary(273.15, 1, 1, 0) unit_database.AddUnit( "My Temperature", "degrees Celsius", "degC", f_base_to_unit, f_unit_to_base, default_category=None, ) f_unit_to_base = MakeCustomaryToBase(2298.35, 5, 9, 0) f_base_to_unit = MakeBaseToCustomary(2298.35, 5, 9, 0) unit_database.AddUnit( "My Temperature", "degree Fahrenheit", "degF", f_base_to_unit, f_unit_to_base, default_category=None, ) f_unit_to_base = MakeCustomaryToBase(0, 1, 1, 0) f_base_to_unit = MakeBaseToCustomary(0, 1, 1, 0) unit_database.AddUnit( "My Temperature", "my Kelvin", "myK", f_base_to_unit, f_unit_to_base, default_category=None, ) unit_database.AddCategory( "My Temperature", quantity_type="My Temperature", valid_units=["myK", "degC", "degF"], default_unit="myK", default_value=2, min_value=0, max_value=15, is_min_exclusive=False, is_max_exclusive=False, caption="My Temperature", ) import numpy values = numpy.array(list(range(10, 13)), numpy.float32) arr = Array("My Temperature", values, "degC") obtained = arr.GetValues("degF") expected = [49.99995041, 51.79994965, 53.5999527] assert approx(obtained) == expected
def testDefaultValues(unit_database_len): # Not raises exception because by default validation is False on the copy operation array = Array("flow rate") assert array.values == [] array = Array(ObtainQuantity("m")) assert array.values == [] with pytest.raises(AssertionError): Array(ObtainQuantity("m"), unit="m")
def testDefaultValues(unit_database_len) -> None: # Not raises exception because by default validation is False on the copy operation array = Array[List[float]]("flow rate") assert array.values == [] array = Array(ObtainQuantity("m")) assert array.values == [] with pytest.raises(AssertionError): Array(ObtainQuantity("m"), unit="m") # type:ignore[call-overload]
def build_constant_initial_velocities_description(values): return case_description.InitialVelocitiesDescription( position_input_type=constants.TableInputType.length, table_length=case_description.VelocitiesContainerDescription( positions=Array([0.0], "m"), velocities={ key: Array([v.value], v.unit) for key, v in values.items() }, ), )
def build_simple_segment(): """ Return an instance of PipeSegmentsDescription with a pre-populated value. The pre-filled value was the default value used by all tests before refactoring, which removed `diameter` and `absolute_roughness` from PipeDescription. """ return case_description.PipeSegmentsDescription( start_positions=Array([0.0], "m"), diameters=Array([0.1], "m"), roughnesses=Array([1e-5], "m"), )
def test_pvt_model_default_behavior(self, default_case, default_well): """ 1) CaseDescription should accept components that uses PvtModel as None while default_model is configured. 2) default_model must be a valid pvt_model defined on pvt_models 3) If default_model is None, all components that uses pvt_model must be assigined. """ from alfasim_sdk._internal.constants import NodeCellType case = attr.evolve( default_case, pvt_models=attr.evolve(default_case.pvt_models, default_model="PVT2"), nodes=[ case_description.NodeDescription( name="Node 1", node_type=NodeCellType.Pressure, pvt_model=None) ], pipes=[ case_description.PipeDescription( name="Pipe 1", pvt_model=None, source="in", target="out", segments=build_simple_segment(), profile=case_description.ProfileDescription( x_and_y=case_description.XAndYDescription( x=Array([0], "m"), y=Array([0], "m"))), ) ], wells=[ attr.evolve( default_well, pvt_model=None, annulus=attr.evolve(default_well.annulus, pvt_model=None), ) ], ) # Check 1) If default_model assigned, elements that uses pvt_model can be None case.ensure_valid_references() assert case.pvt_models.default_model == "PVT2" assert case.nodes[0].pvt_model is None assert case.pipes[0].pvt_model is None assert case.wells[0].pvt_model is None assert case.wells[0].annulus.pvt_model is None # Check 2) default_model must be filled with a valid pvt_model case_with_invalid_default_pvt_model = attr.evolve( case, pvt_models=attr.evolve(case.pvt_models, default_model="Acme"))
def test_to_array(): type_error_msg = r"Expected pair \(values, unit\) or Array" assert to_array(None, is_optional=True) is None with pytest.raises(TypeError, match=type_error_msg): to_array(None) assert to_array(([1, 2, 3], "m")) == Array("length", [1, 2, 3], "m") with pytest.raises(TypeError, match=type_error_msg): to_array(("foo", [1, 2, 3], "m")) with pytest.raises(TypeError, match=type_error_msg): to_array("foo") array = Array("time", [0, 1.1, 2.2], "s") assert to_array(array) is array
def to_array(value: ArrayLike, is_optional: bool = False, *, error_context: Optional[str] = None) -> Array: """ Converter to be used with attr.ib, accepts tuples and Array as input. If `is_optional` is defined, the converter will also accept None, same as `to_scalar`. The `error_context` has the same behavior as in `to_scalar`. Ex.: @attr.s class Foo: bar = attr.id(converter=to_array) Foo(bar=Array([1,2,3],"m")) Foo(bar=([1,2,3],"m")) Foo(bar=((1,2,3),"m")) """ if is_optional and value is None: return value if is_two_element_tuple(value): return Array(*value) elif isinstance(value, Array): return value message = prepare_error_message( f"Expected pair (values, unit) or Array, got {value!r} (type: {type(value)})", error_context, ) raise TypeError(message)
def test_create_array_informing_category(self) -> None: array_molar_fraction = Array.FromScalars( scalars=[Scalar(1, "-"), Scalar(2, "-")], category="percentage" ) assert array_molar_fraction.values == [1, 2] assert array_molar_fraction.unit == "-" assert array_molar_fraction.category == "percentage"
def test_compressor_pressure_table_description_length(): expected_msg = dedent("""\ speed_entries, corrected_mass_flow_rate_entries, pressure_ratio_table and isentropic_efficiency_table must have the same size, got: - 2 items for speed_entries - 2 items for corrected_mass_flow_rate_entries - 2 items for pressure_ratio_table - 1 items for isentropic_efficiency_table """) with pytest.raises(ValueError, match=re.escape(expected_msg)): case_description.CompressorPressureTableDescription( speed_entries=Array([1, 2], "rpm"), corrected_mass_flow_rate_entries=Array([1, 2], "kg/s"), pressure_ratio_table=Array([1, 2], "-"), isentropic_efficiency_table=Array([1], "-"), ) # Check if the defaults values works well case_description.CompressorPressureTableDescription()
def test_table_pump_description_length(): expected_msg = dedent("""\ speeds, void_fractions, flow_rates and pressure_boosts must have the same size, got: - 2 items for speeds - 2 items for void_fractions - 2 items for flow_rates - 1 items for pressure_boosts """) with pytest.raises(ValueError, match=re.escape(expected_msg)): case_description.TablePumpDescription( speeds=Array([1, 2], "rpm"), void_fractions=Array([1, 2], "-"), flow_rates=Array([1, 2], "m3/s"), pressure_boosts=Array([1], "bar"), ) # Check if the defaults values works well case_description.TablePumpDescription()
def test_get_value_and_unit_from_length_and_elevation_description_and_xand_ydescription( ): """ Ensure that GetValueAndUnit returns a pair of values along with their units. """ length_and_elevation = case_description.LengthAndElevationDescription( length=Array([0.0, 5.0, 7.0], "m"), elevation=Array([1.0] * 3, "m")) assert list(length_and_elevation.iter_values_and_unit()) == [ ((0.0, "m"), (1.0, "m")), ((5.0, "m"), (1.0, "m")), ((7.0, "m"), (1.0, "m")), ] x_and_y = case_description.XAndYDescription(x=Array([1.0, 10.0, 100.0], "m"), y=Array([42.0] * 3, "m")) assert list(x_and_y.iter_values_and_unit()) == [ ((1.0, "m"), (42.0, "m")), ((10.0, "m"), (42.0, "m")), ((100.0, "m"), (42.0, "m")), ]
def testNumberInteractions(unit_database_len_time): import numpy m = Quantity.CreateDerived(odict([("Table size", ["m", 1])])) s1 = Array.CreateWithQuantity(m, list(range(10))) s2 = Array.CreateWithQuantity(m, [x + x for x in range(10)]) assert s1 == 0 + s1 assert s1 == s1 + 0 assert s2 == s1 + s1 num_arr = Array.CreateWithQuantity(m, numpy.array([x for x in range(10)])) sum_array = num_arr + s1 assert isinstance(sum_array.values, numpy.ndarray) sum_array2 = num_arr + numpy.array([x for x in range(10)]) assert isinstance(sum_array2.values, numpy.ndarray) tup_arr = Array.CreateWithQuantity(m, tuple([x for x in range(10)])) tup_arr = tup_arr + 1 assert isinstance(tup_arr.values, tuple)
def build_linear_initial_temperatures_description( from_temperature, to_temperature, unit, final_position, position_unit, start_position=0.0, ): return case_description.InitialTemperaturesDescription( position_input_type=constants.TableInputType.length, table_length=case_description.TemperaturesContainerDescription( positions=Array( [ Scalar(start_position, position_unit).GetValue("m"), Scalar(final_position, position_unit).GetValue("m"), ], "m", ), temperatures=Array([from_temperature, to_temperature], unit), ), )
def test_to_curve(): type_error_msg = r"Expected pair \(image_array, domain_array\) or Curve" assert to_curve(None, is_optional=True) is None with pytest.raises(TypeError, match=type_error_msg): to_curve(None) image = Array("length", [1, 2, 3], "m") domain = Array("time", [0, 1.1, 2.2], "s") curve = Curve(image, domain) assert to_curve((([1, 2, 3], "m"), domain)) == curve with pytest.raises( TypeError, match=r"Curve image: Expected pair \(values, unit\) or Array" ): to_curve((("foo", [1, 2, 3], "m"), domain)) with pytest.raises(TypeError, match=type_error_msg): to_curve(("foo", [1, 2, 3], "m")) with pytest.raises(TypeError, match=type_error_msg): to_curve("foo") assert to_curve(curve) is curve
def test_check_profile_description(default_case): """ Ensures that the ProfileDescription only accepts either XAndYDescription or a LengthAndElevationDescription Check 1: length_and_elevation and x_and_y are mutually exclusive """ msg = ( "length_and_elevation and x_and_y are mutually exclusive and you must configure only one of them, got " "length_and_elevation=LengthAndElevationDescription(length=Array(length, [0.0, 5.0, 7.0], m), elevation=Array(length, [1.0, 1.0, 1.0], m)) " "and x_and_y=XAndYDescription(x=Array(length, [1.0, 10.0, 100.0], m), y=Array(length, [42.0, 42.0, 42.0], m))" ) with pytest.raises(ValueError, match=re.escape(msg)): case_description.ProfileDescription( length_and_elevation=case_description. LengthAndElevationDescription(length=Array([0.0, 5.0, 7.0], "m"), elevation=Array([1.0] * 3, "m")), x_and_y=case_description.XAndYDescription( x=Array([1.0, 10.0, 100.0], "m"), y=Array([42.0] * 3, "m")), ) # Empty Profile is allowed. profile = case_description.ProfileDescription(length_and_elevation=None, x_and_y=None) assert profile.length_and_elevation is None assert profile.x_and_y is None # Empty Array is allowed profile_2 = case_description.ProfileDescription( length_and_elevation=case_description.LengthAndElevationDescription( length=Array([], "m"), elevation=Array([], "m")), x_and_y=None, ) assert profile_2.x_and_y is None assert profile_2.length_and_elevation.length.GetValues("m") == [] assert profile_2.length_and_elevation.elevation.GetValues("m") == []
def test_build_compressor_pressure_table_description_valid(): description = build_compressor_pressure_table_description( speed_entries=Array([0, 1000], "rpm"), corrected_mass_flow_rate_entries=Array([1, 2, 3], "kg/s"), pressure_ratio_table=Array([1, 2, 3, 4, 5, 6], "-"), isentropic_efficiency_table=Array([0.4, 0.5, 0.6, 0.7, 0.8, 0.9], "-"), ) assert description.speed_entries == Array([0, 0, 0, 1000, 1000, 1000], "rpm") assert description.corrected_mass_flow_rate_entries == Array( [1, 2, 3, 1, 2, 3], "kg/s") assert description.pressure_ratio_table == Array([1, 2, 3, 4, 5, 6], "-") assert description.isentropic_efficiency_table == Array( [0.4, 0.5, 0.6, 0.7, 0.8, 0.9], "-")