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)
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)
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)