class TestMeshes(TestCase):
    def setUp(self):
        # Define rectangular mesh and its properties
        self.rectangular = RectangularMesh(length=20,
                                           width=10,
                                           height=150,
                                           resolution=4,
                                           filling_fraction=0.5,
                                           units="mm")
        self.rectangular_block_extent = extent(max_extent=[20, 10, 150])
        self.rectangular_filling_extent = [[0, 0, 0], [0.02, 0.01, 0.075]]
        self.rectangular_ncells = [80, 40, 600]
        self.rectangular_volume = 30000

        # Define cylindrical mesh and its properties
        self.cylinder = CylinderMesh(radius=5,
                                     height=20,
                                     resolution=7,
                                     filling_fraction=0.5,
                                     units="cm")
        self.cylinder_stl_extent = extent([-0.05, -0.05, 0], [0.05, 0.05, 0.2])
        self.cylinder_block_extent = extent([-5, -5, 0], [5, 5, 20])
        self.cylinder_filling_extent = [0.05, 0.1]
        self.cylinder_ncells = [70, 70, 140]
        self.cylinder_location = [0, 0, 0.1]
        self.cylinder_volume = 25 * 20 * np.pi

        # Define complex mesh and its properties
        self.complex_radius = 5
        self.complex_height = 10
        self.complex_resolution = 10
        self.complex = ComplexMesh(source_path=get("cone.stl"),
                                   inside_location=[0, 0, 5],
                                   resolution=10,
                                   filling_fraction=0.5,
                                   units="cm")
        self.complex_stl_extent = extent([-0.05, -0.05, 0], [0.05, 0.05, 0.1])
        self.complex_block_extent = extent([-5, -5, 0], [5, 5, 10])
        self.complex_filling_extent = [[-0.05, -0.05, 0], [0.05, 0.05, 0.05]]
        self.complex_ncells = [100, 100, 100]
        self.complex_location = [0, 0, 0.05]
        self.complex_cutoff = 0.5
        self.complex_volume = 1 / 3 * (np.pi * self.complex_radius**2 *
                                       self.complex_height)
        self.complex_volume_cutoff = 1 / 3 * (
            self.complex_radius *
            (1 - self.complex_cutoff))**2 * np.pi * (self.complex_height *
                                                     self.complex_cutoff)

    def test_rectangular(self):
        self.rectangular.write_mesh()
        for ax in self.rectangular_block_extent.keys():
            for direction in self.rectangular_block_extent[ax].keys():
                self.assertEqual(
                    self.rectangular.blockmesh_extent[ax][direction],
                    self.rectangular_block_extent[ax][direction])
        self.assertListEqual(self.rectangular.ncells, self.rectangular_ncells)
        self.assertListEqual(self.rectangular_filling_extent,
                             self.rectangular.filling_extent)
        self.assertEqual(self.rectangular_volume, self.rectangular.volume)

    def test_cylinder(self):
        with TemporaryDirectory() as temp_dir:
            file_path = os.path.join(temp_dir, 'new_surface.stl')
            self.cylinder.write_mesh(temp_dir)
            self.assertTrue(os.path.exists(file_path))
            for ax in self.cylinder_stl_extent.keys():
                for direction in self.cylinder_stl_extent[ax].keys():
                    self.assertEqual(self.cylinder.stl_extent[ax][direction],
                                     self.cylinder_stl_extent[ax][direction])
                    self.assertEqual(
                        self.cylinder.blockmesh_extent[ax][direction],
                        self.cylinder_block_extent[ax][direction])
            self.assertListEqual(self.cylinder.ncells, self.cylinder_ncells)
            self.assertListEqual(self.cylinder.inside_location,
                                 self.cylinder_location)
            self.assertListEqual(self.cylinder_filling_extent,
                                 self.cylinder.filling_extent)
            self.assertAlmostEqual(self.cylinder_volume,
                                   self.cylinder.volume,
                                   places=1)

    def test_complex(self):
        warnings.warn("The destinction between true and "
                      "apparent filling_fractions is currently "
                      "not supported for ComplexMesh-geometries")
        # NOTE: in case that ComplexMesh::cutoff_volume() will
        # deliver reasonable values in the future, please uncomment
        # the following code:
        # self.assertAlmostEqual(
        #    self.complex_volume_cutoff,
        #    self.complex.cutoff_volume(self.complex_cutoff),
        #    delta=3
        # )
        with TemporaryDirectory() as temp_dir:
            file_path = os.path.join(temp_dir, 'new_surface.stl')
            self.complex.write_mesh(temp_dir)
            self.assertTrue(os.path.exists(file_path))
            for ax in self.complex_stl_extent.keys():
                for direction in self.complex_stl_extent[ax].keys():
                    self.assertAlmostEqual(
                        self.complex.stl_extent[ax][direction],
                        self.complex_stl_extent[ax][direction],
                    )
                    self.assertAlmostEqual(
                        self.complex.blockmesh_extent[ax][direction],
                        self.complex_block_extent[ax][direction])
            self.assertListEqual(self.complex.ncells, self.complex_ncells)
            self.assertListEqual(self.complex.inside_location,
                                 self.complex_location)
            self.assertListEqual(self.complex.filling_extent,
                                 self.complex_filling_extent)
            self.assertAlmostEqual(self.complex_volume,
                                   self.complex.volume,
                                   delta=3)
예제 #2
0
class TestMeshes(TestCase):

    def setUp(self):
        # Define rectangular mesh and its properties
        self.rectangular = RectangularMesh(
            x_length=0.02,
            y_length=0.01,
            z_length=0.15,
            resolution=0.001,
            filling_fraction=0.5,
            units="m"
        )
        self.rectangular_max_extent = extent(
            max_extent=[0.02, 0.01, 0.15]
        )
        self.rectangular_filling_extent = extent(
            max_extent=[0.02, 0.01, 0.075]
        )
        self.rectangular_inside_location = [0.01, 0.005, 0.075]
        self.rectangular_volume = 3e-5
        self.rectangular_stl_ref = os.path.join(
            path, 'rectangle_ref.stl'
        )
        self.rectangular_geo_ref = os.path.join(
            path, 'rectangle_ref.geo'
        )
        # Define cylindrical mesh and its properties
        self.cylinder = CylinderMesh(
            xy_radius=0.05,
            z_length=0.2,
            resolution=0.0014,
            filling_fraction=0.5,
            units="m"
        )
        self.cylinder_max_extent = extent(
            [-0.05, -0.05, 0], [0.05, 0.05, 0.2]
        )
        self.cylinder_filling_extent = extent(
            [-0.05, -0.05, 0], [0.05, 0.05, 0.1]
        )
        self.cylinder_inside_location = [0, 0, 0.1]
        self.cylinder_volume = 0.025**2*0.2*np.pi
        self.cylinder_stl_ref = os.path.join(
            path, 'cylinder_ref.stl'
        )
        self.cylinder_geo_ref = os.path.join(
            path, 'cylinder_ref.geo'
        )

        # Define complex mesh and its properties
        self.complex_xy_radius = 5
        self.complex_z_length = 10
        self.complex_cutoff = 0.5
        self.complex = ComplexMesh(
            source_path=os.path.join(path, "cone.stl"),
            inside_location=[0.0, 0.0, 5.0],
            filling_fraction=0.5,
            units="mm"
        )
        self.complex_max_extent = extent(
            [-5.0, -5.0, 0.0], [5.0, 5.0, 10.0]
        )
        self.complex_filling_extent = extent(
            [-5.0, -5.0, 0.0], [5.0, 5.0, 5.0]
        )
        self.complex_inside_location = [0.0, 0.0, 5.0]
        self.complex_volume = 1/3*(np.pi*5**2*10)
        self.complex_volume_cutoff = \
            1/3*(5/10*10*0.5)**2*np.pi*(10*0.5)

    def test_rectangular(self):
        with TemporaryDirectory() as temp_dir:
            stl_path = os.path.join(temp_dir, 'new_surface.stl')
            geo_path = os.path.join(temp_dir, 'new_surface.geo')
            self.rectangular.write_mesh(temp_dir)
            self.assertTrue(os.path.exists(stl_path))
            self.assertTrue(os.path.exists(geo_path))
            for ax in self.rectangular_max_extent.keys():
                for direction in self.rectangular_max_extent[ax].keys():
                    self.assertEqual(
                        self.rectangular.max_extent[ax][direction],
                        self.rectangular_max_extent[ax][direction]
                    )
                    self.assertEqual(
                        self.rectangular.filling_extent[ax][direction],
                        self.rectangular_filling_extent[ax][direction]
                    )
            self.assertListEqual(
                self.rectangular.inside_location,
                self.rectangular_inside_location
            )
            self.assertEqual(
                self.rectangular_volume,
                self.rectangular.volume
            )
            self.compare_files(
                stl_path, self.rectangular_stl_ref
            )
            self.compare_files(
                geo_path, self.rectangular_geo_ref
            )

    def test_cylinder(self):
        with TemporaryDirectory() as temp_dir:
            stl_path = os.path.join(temp_dir, 'new_surface.stl')
            geo_path = os.path.join(temp_dir, 'new_surface.geo')
            self.cylinder.write_mesh(temp_dir)
            self.assertTrue(os.path.exists(stl_path))
            self.assertTrue(os.path.exists(geo_path))
            for ax in self.cylinder_max_extent.keys():
                for direction in self.cylinder_max_extent[ax].keys():
                    self.assertEqual(
                        self.cylinder.max_extent[ax][direction],
                        self.cylinder_max_extent[ax][direction]
                    )
                    self.assertEqual(
                        self.cylinder.filling_extent[ax][direction],
                        self.cylinder_filling_extent[ax][direction]
                    )
            self.assertListEqual(
                self.cylinder.inside_location,
                self.cylinder_inside_location
            )
            self.assertAlmostEqual(
                self.cylinder_volume,
                self.cylinder.volume,
                places=1
            )
            self.compare_files(
                stl_path, self.cylinder_stl_ref
            )
            self.compare_files(
                geo_path, self.cylinder_geo_ref
            )

    def test_complex(self):
        warnings.warn(
                "The destinction between true and "
                "apparent filling_fractions is currently "
                "not supported for ComplexMesh-geometries"
        )
        # NOTE: in case that ComplexMesh::cutoff_volume() will
        # deliver reasonable values in the future, please uncomment
        # the following code:
        # self.assertAlmostEqual(
        #    self.complex_volume_cutoff,
        #    self.complex.cutoff_volume(self.complex_cutoff),
        #    delta=3
        # )
        self.complex.inspect_file()
        for ax in self.complex_max_extent.keys():
            for direction in self.complex_max_extent[ax].keys():
                self.assertAlmostEqual(
                    self.complex.max_extent[ax][direction],
                    self.complex_max_extent[ax][direction],
                )
                self.assertAlmostEqual(
                    self.complex.filling_extent[ax][direction],
                    self.complex_filling_extent[ax][direction],
                )
        self.assertListEqual(
            self.complex.inside_location,
            self.complex_inside_location
        )
        self.assertAlmostEqual(
            self.complex_volume,
            self.complex.volume,
            delta=3
        )

    def compare_files(self, target_path, ref_path):
        with open(target_path, "r") as target, \
                open(ref_path, "r") as ref:
            target_text = "".join(target.read().split())
            ref_text = "".join(ref.read().split())
            self.assertEqual(target_text, ref_text)
예제 #3
0
class TestPUFoamDataSource(TestCase):
    def setUp(self):
        self.factory = PUFoamFactory(plugin={'id': '0', 'name': 'test'})
        self.data_source = self.factory.create_data_source()
        self.model = self.factory.create_model()
        self.rectangle = RectangularMesh(length=10,
                                         width=2,
                                         height=20,
                                         resolution=5,
                                         units="cm")
        self.cylinder = CylinderMesh(radius=6, resolution=10, units="cm")
        self.complex = ComplexMesh(source_path=get("cone.stl"),
                                   inside_location=[0, 0, 2.5],
                                   units="cm")

    def test__calculate_filling_fraction(self):

        formulation = ProbeFormulation()
        formulation.chemicals = [formulation.chemicals[2]]

        self.model.use_mass = True
        foam_volume = 1e-4
        rectangle_volume = 4e-4
        cylinder_volume = 1.1304e-3
        complex_volume = 2.6180e-4

        filling_fraction = self.data_source._calculate_filling_fraction(
            self.model, formulation, self.rectangle)
        self.assertAlmostEqual(foam_volume / rectangle_volume,
                               filling_fraction)
        filling_fraction = self.data_source._calculate_filling_fraction(
            self.model, formulation, self.cylinder)
        self.assertAlmostEqual(foam_volume / cylinder_volume,
                               filling_fraction,
                               delta=1e-4)

        self.model.foam_mass = 394.78
        self.model.foam_volume = 0.5
        foam_volume = 3.9478e-4

        filling_fraction = self.data_source._calculate_filling_fraction(
            self.model, formulation, self.complex)
        self.assertAlmostEqual(foam_volume / complex_volume,
                               filling_fraction,
                               delta=0.01)
        warnings.warn("The destinction between true and apparent "
                      "filling_fractions is currently not supported "
                      "for ComplexMesh-geometries.")
        # NOTE: in case that ComplexMesh::cutoff_volume() will
        # deliver reasonable values in the future, please delete
        # the assertion before the warning and uncomment the
        # following code:
        # self.assertAlmostEqual(
        #    foam_volume/complex_volume,
        #    filling_fraction,
        #    delta=0.01
        # )

        self.model.use_mass = False

        filling_fraction = self.data_source._calculate_filling_fraction(
            self.model, formulation, self.rectangle)
        self.assertAlmostEqual(self.model.foam_volume, filling_fraction)
        filling_fraction = self.data_source._calculate_filling_fraction(
            self.model, formulation, self.cylinder)
        self.assertAlmostEqual(self.model.foam_volume, filling_fraction)
        filling_fraction = self.data_source._calculate_filling_fraction(
            self.model, formulation, self.complex)
        self.assertAlmostEqual(self.model.foam_volume, filling_fraction)

    def test__update_simulation_data(self):
        is_updated = self.data_source._update_simulation_data(
            DataValue(type="A_OH", value=2))
        self.assertTrue(is_updated)
        self.assertEqual(
            2, self.data_source.data_dicts[0].data["GellingConstants"]["A_OH"])

        is_updated = self.data_source._update_simulation_data(
            DataValue(type="B_OH", value=2))
        self.assertFalse(is_updated)

    def test__prepare_mesh(self):
        def return_url_name(url, **kwargs):
            return ProbeResponse(text=url)

        with TemporaryDirectory() as temp_dir:
            self.model.simulation_directory = temp_dir

            self.data_source.update_mesh_data(self.rectangle, self.model)
            with mock.patch('requests.post') as mock_post, \
                    mock.patch('requests.get') as mock_get:
                mock_post.side_effect = return_url_name
                mock_get.side_effect = return_url_name
                session = PUFoamSession()
                self.assertEqual(
                    'http://0.0.0.0:5000/block_mesh',
                    self.data_source._prepare_mesh(session, self.model).text)

            self.data_source.update_mesh_data(self.cylinder, self.model)
            with mock.patch('requests.post') as mock_post, \
                    mock.patch('requests.get') as mock_get:
                mock_post.side_effect = return_url_name
                mock_get.side_effect = return_url_name
                session = PUFoamSession()
                self.assertEqual(
                    'http://0.0.0.0:5000/transform/constant/triSurface',
                    self.data_source._prepare_mesh(session, self.model).text)

            self.data_source.update_mesh_data(self.complex, self.model)
            with mock.patch('requests.post') as mock_post, \
                    mock.patch('requests.get') as mock_get:
                mock_post.side_effect = return_url_name
                mock_get.side_effect = return_url_name
                session = PUFoamSession()
                self.assertEqual(
                    'http://0.0.0.0:5000/transform/constant/triSurface',
                    self.data_source._prepare_mesh(session, self.model).text)

    def test_update_formulation_data(self):
        formulation = ProbeFormulation()
        data_dict = self.data_source.data_dicts[0]
        self.data_source.update_formulation_data(formulation)

        self.assertAlmostEqual(1118.44312716,
                               data_dict.data["GellingConstants"]["initCOH"])
        self.assertAlmostEqual(11184.43127166,
                               data_dict.data["GellingConstants"]["initCNCO"])
        self.assertAlmostEqual(50951.29801538,
                               data_dict.data["GellingConstants"]["initCW"])
        self.assertAlmostEqual(
            0.00050125313,
            data_dict.data["GenericConstants"]["initBlowingAgent"])
        self.assertAlmostEqual(
            2231.29403869, data_dict.data["GenericConstants"]["rhoPolymer"])
        self.assertAlmostEqual(
            10000, data_dict.data["GenericConstants"]["rhoBlowingAgent"])

    def test_update_mesh_data(self):
        with TemporaryDirectory() as temp_dir:
            self.model.simulation_directory = temp_dir
            blockmesh = self.data_source.data_dicts[1]
            snappyhex = self.data_source.data_dicts[4]

            self.data_source.update_mesh_data(self.rectangle, self.model)
            for ax in self.rectangle.blockmesh_extent.keys():
                for direction in self.rectangle.blockmesh_extent[ax].keys():
                    self.assertEqual(
                        self.rectangle.blockmesh_extent[ax][direction],
                        blockmesh.data[ax + direction])
            self.assertListEqual(self.rectangle.ncells,
                                 blockmesh.data["blocks"][0][2])
            self.assertEqual(0.01, blockmesh.data["convertToMeters"])

            self.rectangle.units = 'mm'
            self.data_source.update_mesh_data(self.rectangle, self.model)
            self.assertEqual(0.001, blockmesh.data["convertToMeters"])

            self.rectangle.units = 'm'
            self.data_source.update_mesh_data(self.rectangle, self.model)
            self.assertEqual(1.0, blockmesh.data["convertToMeters"])

            self.data_source.update_mesh_data(self.cylinder, self.model)
            box = snappyhex.data['geometry']["refinementBox"]
            loc = snappyhex.data['castellatedMeshControls']['locationInMesh']
            for num, ax in enumerate(self.cylinder.blockmesh_extent.keys()):
                for direction in self.cylinder.blockmesh_extent[ax].keys():
                    self.assertEqual(
                        self.cylinder.blockmesh_extent[ax][direction],
                        blockmesh.data[ax + direction])
                    self.assertEqual(self.cylinder.stl_extent[ax][direction],
                                     box[direction][num])
            self.assertListEqual(self.cylinder.inside_location, loc)
            self.assertListEqual(self.cylinder.ncells,
                                 blockmesh.data["blocks"][0][2])
            self.assertListEqual(self.cylinder.ncells,
                                 blockmesh.data["blocks"][0][2])
            self.assertEqual(0.01, blockmesh.data["convertToMeters"])

            self.cylinder.units = 'mm'
            self.data_source.update_mesh_data(self.cylinder, self.model)
            self.assertEqual(0.001, blockmesh.data["convertToMeters"])

            self.cylinder.units = 'm'
            self.data_source.update_mesh_data(self.cylinder, self.model)
            self.assertEqual(1.0, blockmesh.data["convertToMeters"])

            self.data_source.update_mesh_data(self.complex, self.model)
            box = snappyhex.data['geometry']["refinementBox"]
            loc = snappyhex.data['castellatedMeshControls']['locationInMesh']
            for num, ax in enumerate(self.complex.blockmesh_extent.keys()):
                for direction in self.complex.blockmesh_extent[ax].keys():
                    self.assertEqual(
                        self.complex.blockmesh_extent[ax][direction],
                        blockmesh.data[ax + direction])
                    self.assertEqual(self.complex.stl_extent[ax][direction],
                                     box[direction][num])
            self.assertListEqual(self.complex.inside_location, loc)
            self.assertListEqual(self.complex.ncells,
                                 blockmesh.data["blocks"][0][2])
            self.assertEqual(0.01, blockmesh.data["convertToMeters"])

            self.complex.units = 'mm'
            self.data_source.update_mesh_data(self.complex, self.model)
            self.assertEqual(0.001, blockmesh.data["convertToMeters"])

            self.complex.units = 'm'
            self.data_source.update_mesh_data(self.complex, self.model)
            self.assertEqual(1.0, blockmesh.data["convertToMeters"])

    def test_update_control_data(self):
        self.model.time_steps = 100

        self.data_source.update_control_data(self.model)
        self.assertEqual(100, self.data_source.data_dicts[2].data["endTime"])

    def test_update_fields_data(self):
        fraction = 0.2
        with TemporaryDirectory() as temp_dir:
            fields_dict = self.data_source.data_dicts[3]
            self.rectangle.write_mesh()
            self.cylinder.write_mesh(temp_dir)
            self.complex.write_mesh(temp_dir)

            self.data_source.update_fields_data(self.rectangle, fraction)
            self.assertListEqual(
                [[0, 0, 0],
                 [
                     self.rectangle.length * self.rectangle.convert_to_meters,
                     self.rectangle.width * self.rectangle.convert_to_meters,
                     round(
                         self.rectangle.height *
                         self.rectangle.convert_to_meters * fraction, 2)
                 ]], fields_dict.regions[0]['boxToCell']['box'])

            self.data_source.update_fields_data(self.cylinder, fraction)
            self.assertEqual(
                self.cylinder.radius * self.cylinder.convert_to_meters,
                fields_dict.regions[1]['cylinderToCell']['radius'])
            self.assertEqual(
                fraction * self.cylinder.height *
                self.cylinder.convert_to_meters,
                fields_dict.regions[1]['cylinderToCell']['p2'][-1])

            self.data_source.update_fields_data(self.complex, fraction)
            self.assertListEqual([
                [self.complex.stl_extent[ax]["min"] for ax in ["x", "y", "z"]],
                [
                    self.complex.stl_extent[ax]["max"] * fraction
                    if ax == "z" else self.complex.stl_extent[ax]["max"]
                    for ax in ["x", "y", "z"]
                ]
            ], fields_dict.regions[0]['boxToCell']['box'])

    def test_update_reaction_data(self):
        reaction_dict = self.data_source.data_dicts[0]
        mapping = {"A_OH": 10, "E_OH": 5, "deltaOH": 1, "gellingPoint": 2.5}
        self.data_source.update_reaction_data(mapping)
        self.assertEqual(10, reaction_dict.data["GellingConstants"]["A_OH"])
        self.assertEqual(5, reaction_dict.data["GellingConstants"]["E_OH"])
        self.assertEqual(1, reaction_dict.data["EnthalpyConstants"]["deltaOH"])
        self.assertEqual(
            2.5, reaction_dict.data["GellingConstants"]["gellingPoint"])

        mapping = {"A_W": 10, "E_W": 5, "deltaW": 1, "latentHeat": 2.5}

        self.data_source.update_reaction_data(mapping)
        self.assertEqual(10, reaction_dict.data["BlowingConstants"]["A_W"])
        self.assertEqual(5, reaction_dict.data["BlowingConstants"]["E_W"])
        self.assertEqual(1, reaction_dict.data["EnthalpyConstants"]["deltaW"])
        self.assertEqual(2.5,
                         reaction_dict.data["EnthalpyConstants"]["latentHeat"])

    def test_verify(self):
        errors = self.model.verify()
        self.assertEqual(2, len(errors))

        self.model.foam_volume = 2
        errors = self.model.verify()
        self.assertEqual(3, len(errors))
        self.assertEqual("PUFoam initial volume must be between 0 - 1",
                         errors[-1].global_error)

        self.model.foam_volume = -1
        errors = self.model.verify()
        self.assertEqual(3, len(errors))
        self.assertEqual("PUFoam initial volume must be between 0 - 1",
                         errors[-1].global_error)

    def test_change_slots(self):
        slots = self.data_source.slots(self.model)

        self.assertEqual(4, len(slots[0]))
        self.assertEqual(1, len(slots[1]))

        self.model.input_method = 'Parameter'
        slots = self.data_source.slots(self.model)

        self.assertEqual(5, len(slots[0]))
        self.assertEqual(1, len(slots[1]))
        self.assertEqual('FILLING_FRACTION', slots[0][-1].type)

        self.model.use_mass = True
        slots = self.data_source.slots(self.model)

        self.assertEqual(5, len(slots[0]))
        self.assertEqual(1, len(slots[1]))
        self.assertEqual('MASS', slots[0][-1].type)