def test_weighted_sum(self): """Tests weighted sum to make sure weights are implemented correctly""" fitness_params = { 'goal_fos': 1.5, 'critical_nodes': np.array([1]), 'w_fos': 100, 'w_mass': 1, 'w_deflection': 10 } f = FitnessFunction('weighted_sum', fitness_params) mass = 5 deflection = np.array([[1, 0, 0, 0, 0, 0], [np.sqrt(2), np.sqrt(2), 0, 0, 0, 0]]) fos = np.array([.5, 5]) t1 = Truss(0, 0, 0, 0) t1.mass = mass t1.deflection = deflection t1.fos = fos t1 = f(t1) self.assertAlmostEqual(t1.fitness_score, 125) t0 = Truss(0, 0, 0, 0) fitness_params['critical_nodes'] = np.array([]) fos = np.array([]) t0.mass = mass t0.deflection = deflection t0.fos = fos t0 = f(t0) self.assertAlmostEqual(t0.fitness_score, 185)
def test_sphere(self): """Tests to make sure min is where it should be.""" x = np.zeros(10) y = np.ones(10) t0 = Truss(x, x, 0, 0) t1 = Truss(y, y, 0, 0) f = FitnessFunction('sphere', {}) t0 = f(t0) t1 = f(t1) self.assertAlmostEqual(t0.fitness_score, 0) self.assertAlmostEqual(t1.fitness_score, 10)
def test_rosenbrock(self): """Tests to make sure min for rosenbrock function is where it should be. """ x = np.zeros(11) y = np.ones(11) t0 = Truss(x, x, 0, 0) t1 = Truss(y, y, 0, 0) f = FitnessFunction('rosenbrock', {}) t0 = f(t0) t1 = f(t1) self.assertAlmostEqual(t0.fitness_score, 5) self.assertAlmostEqual(t1.fitness_score, 0)
def testProgressBar(self): """Tests single progress bar for genalg. """ user_spec_nodes = np.array([[]]).reshape(0, 3) nodes = np.array([[1, 2, 3], [2, 3, 4]]) edges = np.array([[0, 1]]) properties = np.array([[0, 3]]) pop_size = 10 population = [ Truss(user_spec_nodes, nodes, edges, properties) for i in range(pop_size) ] for truss in population: truss.fitness_score = np.random.random() population.sort(key=lambda x: x.fitness_score) GA = GenAlg(config) GA.population = population progress_truss = False progress_fitness = False # dumb GA run num_generations = 20 progress = ProgMon(progress_fitness, progress_truss, num_generations) # Loop over all generations: for current_gen in tqdm(range(num_generations)): progress.progress_monitor(current_gen, population) time.sleep(0.05) for truss in GA.population: truss.fitness_score = truss.fitness_score + 5.0 return GA.population[0], GA.pop_progress
def test_axial_load_z(self): """Tests simple straight beam under axial forces""" p = 5000 # load in newtons L = 4 # length in meters matl = 1 rand_nodes = np.array([]).reshape(0, 3) # no random nodes user_spec_nodes = np.array([[0, 0, 0], [0, 0, L]]) edges = np.array([[0, 1]]) properties = np.array([matl]) truss = Truss(user_spec_nodes, rand_nodes, edges, properties) dof = np.array([[1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0]]).reshape(2, 6, 1) load = np.array([[0, 0, 0, 0, 0, 0], [0, 0, p, 0, 0, 0]]).reshape(2, 6, 1) beam_dict = utilities.beam_file_parser('gastop-config/properties.csv') bdry = {'loads': load, 'fixtures': dof} evaluator = Evaluator('mat_struct_analysis_DSM', 'mass_basic', 'interference_ray_tracing', 'cost_calc', bdry, beam_dict) evaluator(truss) A = beam_dict['x_section_area'][matl] E = beam_dict['elastic_modulus'][matl] sigma = p / A fos_true = beam_dict['yield_strength'][matl] / sigma strain = sigma / E deflection_true = np.array([[[0], [0], [0], [0], [0], [0]], [[0], [0], [strain * L], [0], [0], [0]]]) np.testing.assert_almost_equal(truss.mass, A * L * beam_dict['density'][matl]) np.testing.assert_array_almost_equal(truss.fos, fos_true) np.testing.assert_array_almost_equal(truss.deflection, deflection_true)
def test_duplicate_members(self): """Tests a truss with duplicate members between two nodes, to ensure those members are not counted""" p = 9000 # axial load in newtons f = 1750 # transverse load in newtons T = 72 # torsion in newton-meters L = .12 # length in meters matl = 4 rand_nodes = np.array([]).reshape(0, 3) # no random nodes user_spec_nodes = np.array([[0, 0, 0], [L, 0, 0]]) edges = np.array([[0, 1], [0, 1]]) properties = np.array([matl, matl]) truss = Truss(user_spec_nodes, rand_nodes, edges, properties) dof = np.array([[1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0]]).reshape(2, 6, 1) load = np.array([[0, 0, 0, 0, 0, 0], [p, -f, 0, -T, 0, 0]]).reshape(2, 6, 1) beam_dict = utilities.beam_file_parser('gastop-config/properties.csv') bdry = {'loads': load, 'fixtures': dof} evaluator = Evaluator('mat_struct_analysis_DSM', 'mass_basic', 'blank_test', 'cost_calc', bdry, beam_dict) evaluator(truss) A = beam_dict['x_section_area'][matl] fos_true = 4.57 np.testing.assert_almost_equal(truss.mass, A * L * beam_dict['density'][matl]) np.testing.assert_array_almost_equal(truss.fos[0], fos_true, 2) np.testing.assert_almost_equal(truss.cost, L * beam_dict['cost'][matl])
def test_unconnected_node(self): """Tests truss with 3 nodes and 1 connection 3rd node is not connected to the other two, and is not loaded or supported should return nonzero fos, as unconnected node is not loaded so is ignored """ p = 10000 # load in newtons L = 4 # length in meters matl = 2 rand_nodes = np.array([]).reshape(0, 3) # no random nodes user_spec_nodes = np.array([[0, 0, 0], [L, 0, 0], [0, L, 0]]) edges = np.array([[0, 1]]) properties = np.array([matl]) truss = Truss(user_spec_nodes, rand_nodes, edges, properties) dof = np.array([[1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]).reshape(3, 6, 1) load = np.array([[0, 0, 0, 0, 0, 0], [p, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]).reshape(3, 6, 1) beam_dict = utilities.beam_file_parser('gastop-config/properties.csv') bdry = {'loads': load, 'fixtures': dof} evaluator = Evaluator('mat_struct_analysis_DSM', 'mass_basic', 'blank_test', 'cost_calc', bdry, beam_dict) evaluator(truss) A = beam_dict['x_section_area'][matl] sigma = p / A fos_true = beam_dict['yield_strength'][matl] / sigma np.testing.assert_almost_equal(truss.mass, A * L * beam_dict['density'][matl]) np.testing.assert_array_almost_equal(truss.fos, fos_true) np.testing.assert_almost_equal(truss.cost, L * beam_dict['cost'][matl]) np.testing.assert_almost_equal(truss.cost, L * beam_dict['cost'][matl])
def test_unsupported_load(self): """Tests straight beam under axial forces with additional unsupported load Should return fos = 0, as stiffness matrix is singular """ p = 50000 # load in newtons L = 10 # length in meters matl = 0 rand_nodes = np.array([]).reshape(0, 3) # no random nodes user_spec_nodes = np.array([[0, 0, 0], [L, 0, 0], [0, 0, L]]) edges = np.array([[0, 1]]) properties = np.array([matl]) truss = Truss(user_spec_nodes, rand_nodes, edges, properties) dof = np.array([[1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]).reshape(3, 6, 1) load = np.array([[0, 0, 0, 0, 0, 0], [p, 0, 0, 0, 0, 0], [p, 0, 0, 0, 0, 0]]).reshape(3, 6, 1) beam_dict = utilities.beam_file_parser('gastop-config/properties.csv') bdry = {'loads': load, 'fixtures': dof} evaluator = Evaluator('mat_struct_analysis_DSM', 'mass_basic', 'blank_test', 'cost_calc', bdry, beam_dict) evaluator(truss) fos_true = 0 np.testing.assert_array_almost_equal(truss.fos, fos_true)
def test_combined_load(self): """Tests beam under combined loading Beam is fixed at one end, and loads applied at the other, creating bending moment, torsion and shear forces. From Shigley's Mechanical Engineering Design, 10th ed, pp 247-248 """ p = 9000 # axial load in newtons f = 1750 # transverse load in newtons T = 72 # torsion in newton-meters L = .12 # length in meters matl = 4 rand_nodes = np.array([]).reshape(0, 3) # no random nodes user_spec_nodes = np.array([[0, 0, 0], [L, 0, 0]]) edges = np.array([[0, 1]]) properties = np.array([matl]) truss = Truss(user_spec_nodes, rand_nodes, edges, properties) dof = np.array([[1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0]]).reshape(2, 6, 1) load = np.array([[0, 0, 0, 0, 0, 0], [p, -f, 0, -T, 0, 0]]).reshape(2, 6, 1) beam_dict = utilities.beam_file_parser('gastop-config/properties.csv') bdry = {'loads': load, 'fixtures': dof} evaluator = Evaluator('mat_struct_analysis_DSM', 'mass_basic', 'blank_test', 'cost_calc', bdry, beam_dict) evaluator(truss) A = beam_dict['x_section_area'][matl] fos_true = 4.57 np.testing.assert_almost_equal(truss.mass, A * L * beam_dict['density'][matl]) np.testing.assert_array_almost_equal(truss.fos, fos_true, 2) np.testing.assert_almost_equal(truss.cost, L * beam_dict['cost'][matl])
def generate_random(self): # Dan '''Generates and returns new truss objects with random properties The random method first determines the desired ranges of all values that will be calculated. Then, random numbers for the node locations, connections, and properties are all determined with the numpy.random methods. Args: None Returns: (Truss object): Truss object with the newly determined values ''' # Generates new random chromosomes with uniform distribution num_rand_nodes = self.random_params['num_rand_nodes'] num_rand_edges = self.random_params['num_rand_edges'] user_spec_nodes = self.random_params['user_spec_nodes'] num_user_spec_nodes = user_spec_nodes.shape[0] domain = self.random_params['domain'] # First, generate the new nodes: new_nodes = np.random.uniform(domain[0], domain[1], (num_rand_nodes, 3)) # 2nd, generate the new edges between the nodes: new_edges = np.random.randint(num_rand_nodes + num_user_spec_nodes, size=(num_rand_edges, 2)) new_properties = np.random.randint( self.random_params['num_material_options'], size=(num_rand_edges)) return Truss(user_spec_nodes, new_nodes, new_edges, new_properties)
def load_state(dest_config='config.json', dest_pop='population.json'): # Cristian '''Loads the current population and config settings from JSON files. Args: dest_config (string): Path to config data file. dest_pop (string): Path to population data file. Returns: ga (GenAlg object) ''' # Load config data with open(dest_config, 'r') as f: config_loaded = json.load(f) config = json.loads(config_loaded, object_hook=encoders.numpy_decoder) # Load population data with open(dest_pop, 'r') as f: pop_loaded = json.load(f) population = json.loads(pop_loaded, object_hook=encoders.numpy_decoder) population = (Truss(**dct) for dct in population) ga = GenAlg(config) ga.population = population return ga
def test_truss_print(self): """Prints a truss as formatted text""" user_spec_nodes = np.array([[-1, -1, 0], [-1, 1, 0]]) rand_nodes = np.array([[1, 1, 0], [1, -1, 0], [0, 0, 1]]) edges = np.array([[0, 1], [1, 2], [2, 3], [3, 0], [0, 4], [1, 4], [2, 4], [3, 4]]) properties = np.zeros(8) truss = Truss(user_spec_nodes, rand_nodes, edges, properties) print(truss)
def test_truss_plot(self): """Plots a pyramidal truss with loads and deflections.""" user_spec_nodes = np.array([[-1, -1, 0], [-1, 1, 0]]) rand_nodes = np.array([[1, 1, 0], [1, -1, 0], [0, 0, 1]]) edges = np.array([[0, 1], [1, 2], [2, 3], [3, 0], [0, 4], [1, 4], [2, 4], [3, 4]]) properties = np.zeros(8) loads = np.concatenate((np.zeros( (4, 6)), np.array([[-100, 0, -100, 0, 0, 0]])), axis=0).reshape(5, 6, 1) fixtures = np.concatenate((np.ones((4, 6)), np.zeros((1, 6))), axis=0).reshape(5, 6, 1) load_scale = .005 def_scale = 10 truss = Truss(user_spec_nodes, rand_nodes, edges, properties) truss.deflection = np.concatenate((np.zeros( (4, 6)), np.array([[-.01, 0, -.01, 0, 0, 0]])), axis=0).reshape(5, 6, 1) domain = np.array([[-1.5, 1.5], [-1.5, 1.5], [0, 1.5]]).T truss.plot(domain, loads, fixtures, True, load_scale, def_scale)
def __call__(self, truss_1, truss_2): """Calls a crossover object on two trusses to combine them. Crossover object must have been instantiated specifying which methods to use. Args: truss_1 (Truss object): First truss to be combined. truss_2 (Truss object): Second truss to be combined. Returns: child_1, child_2 (Truss objects): Children trusses produced by crossover. """ user_spec_nodes = self.params['user_spec_nodes'] child_1 = Truss(user_spec_nodes, 0, 0, 0) child_2 = Truss(user_spec_nodes, 0, 0, 0) child_1.rand_nodes, child_2.rand_nodes = self.node_method( truss_1.rand_nodes, truss_2.rand_nodes, **self.params['node_crossover_params']) child_1.edges, child_2.edges = self.edge_method( truss_1.edges, truss_2.edges, **self.params['edge_crossover_params']) child_1.properties, child_2.properties = self.property_method( truss_1.properties, truss_2.properties, **self.params['property_crossover_params']) return child_1, child_2
def __call__(self, truss): """Calls a mutator object on a truss to change it. Mutator object must have been instantiated specifying which methods to use. Args: truss (Truss object): Truss to be mutated. Returns: child (Truss object): Child truss produced by mutation. """ user_spec_nodes = self.params['user_spec_nodes'] child = Truss(user_spec_nodes, 0, 0, 0) child.rand_nodes = self.node_method( truss.rand_nodes, **self.params['node_mutator_params']) child.edges = self.edge_method(truss.edges, **self.params['edge_mutator_params']) child.properties = self.property_method( truss.properties, **self.params['property_mutator_params']) return child
def testInvSqrRankProp(self): '''Tests the inverse_square_rank_probability method of Selector() ''' nodes = np.array([[1, 2, 3], [2, 3, 4]]) edges = np.array([[0, 1]]) properties = np.array([[0, 3]]) pop_size = int(1e3) population = [ Truss(nodes, nodes, edges, properties) for i in range(pop_size) ] for truss in population: truss.fitness_score = random.random() population.sort(key=lambda x: x.fitness_score) # print([x.fitness_score for x in population]) sel_params = { 'method': 'inverse_square_rank_probability', 'method_params': {} } selector = Selector(sel_params) num_parents = int(1e6) parents = selector(num_parents, population) unique, counts = np.unique(parents, return_counts=True) # unique_missing = [x for x in range(pop_size) if x not in unique] # counts = np.append(counts,np.zeros(len(unique_missing))) # counts = np.insert(counts,unique_missing,np.zeros(len(unique_missing))) # print(counts.size) distribution = counts / num_parents # print(distribution) true_distribution = 1 / np.sqrt(np.array(range(1, pop_size + 1))) true_sum = np.sum(true_distribution) true_distribution = true_distribution / true_sum # print(true_distribution) error = true_distribution - distribution max_err = np.amax(error) # print(max_err) # print(parents,parents.shape,parents.max()) self.assertAlmostEqual(max_err / pop_size, 0, places=3) self.assertEqual(num_parents, len(parents))
def test_fixtures(self): """Tests applied loads with various dof fixed""" p = 1000 # load in newtons L = 1 # length in meters matl = 2 rand_nodes = np.array([]).reshape(0, 3) # no random nodes user_spec_nodes = np.array([[0, 1, 0], [L, 0, 0], [0, -1, 0]]) edges = np.array([[0, 1], [2, 1]]) properties = matl * np.ones((edges.shape[0])).astype(int) truss = Truss(user_spec_nodes, rand_nodes, edges, properties) dof = np.array([[1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0]]).reshape(3, 6, 1) load = np.array([[0, 0, 0, 0, 0, 0], [0, 0, -p, 0, 0, 0], [0, 0, 0, 0, 0, 0]]).reshape(3, 6, 1) beam_dict = utilities.beam_file_parser('gastop-config/properties.csv') bdry = {'loads': load, 'fixtures': dof} evaluator = Evaluator('mat_struct_analysis_DSM', 'mass_basic', 'blank_test', 'cost_calc', bdry, beam_dict) evaluator(truss)
def load_progress_history(path_progress_history='progress_history.json'): '''Loads the population history (progress_history) from a JSON file. Args: path_progress_history (string): Path to progress_history data file. Returns: progress_history (dict): History of each generation, including generation number, fittest truss, etc. ''' # Load progress_history data with open(path_progress_history, 'r') as f: progress_history_loaded = json.load(f) progress_history = json.loads(progress_history_loaded, object_hook=encoders.numpy_decoder) # Bundle truss dictionaries as Truss objects for gen in progress_history.keys(): progress_history[gen]['Best Truss'] = Truss( **progress_history[gen]['Best Truss']) return progress_history
def testProgressPlotClass(self): """Tests fitness plot for progmon """ user_spec_nodes = np.array([[]]).reshape(0, 3) nodes = np.array([[1, 2, 3], [2, 3, 4]]) edges = np.array([[0, 1]]) properties = np.array([[0, 3]]) pop_size = 10 population = [ Truss(user_spec_nodes, nodes, edges, properties) for i in range(pop_size) ] for truss in population: truss.fitness_score = np.random.random() population.sort(key=lambda x: x.fitness_score) # print([x.fitness_score for x in population]) GA = GenAlg(config) GA.population = population progress_fitness = True progress_truss = False num_generations = 20 progress = ProgMon(progress_fitness, progress_truss, num_generations) # # Loop over all generations: for current_gen in range(num_generations): progress.progress_monitor(current_gen, population) for truss in GA.population: #truss.fos = np.random.random() truss.fitness_score = truss.fitness_score + 5.0 # plt.show() # sfr, keep plot from closing right after this completes, terminal will hang until this is closed return GA.population[0], GA.pop_progress
def testTournament(self): '''Tests the tournament method of Selector() ''' nodes = np.array([[1, 2, 3], [2, 3, 4]]) edges = np.array([[0, 1]]) properties = np.array([[0, 3]]) pop_size = int(1e2) population = [ Truss(nodes, nodes, edges, properties) for i in range(pop_size) ] for truss in population: truss.fitness_score = random.random() population.sort(key=lambda x: x.fitness_score) # print([x.fitness_score for x in population]) sel_params = { 'method': 'tournament', 'method_params': { 'tourn_size': 25, 'tourn_prob': 0.5 } } selector = Selector(sel_params) num_parents = int(1e2) parents = selector(num_parents, population) unique, counts = np.unique(parents, return_counts=True) distribution = counts / num_parents # print(distribution) # print(parents,parents.shape,parents.max()) self.assertEqual(num_parents, len(parents)) self.assertTrue(parents.max() < pop_size)