def Is_Convergence(self): ''' # relative difference in a mean stress for the last 5 iterations must be < tolerance # i >= iterations_limit ''' global i, i_violated, difference_last, continue_iterations, check_tolerance, mass_goal_i if len(FI_mean) > 5: difference_last = [] for last in range(1, 6): difference_last.append( abs(FI_mean[i] - FI_mean[i - last]) / FI_mean[i]) difference = max(difference_last) if check_tolerance is True: print( "maximum relative difference in FI_mean for the last 5 iterations = {}" .format(difference)) if difference < tolerance: continue_iterations = False elif FI_mean[i] == FI_mean[i - 1] == FI_mean[i - 2]: continue_iterations = False print("FI_mean[i] == FI_mean[i-1] == FI_mean[i-2]") # start of the new iteration or finish of the iteration process if continue_iterations is False or i >= iterations_limit: # break return True else: # export the present mesh if save_iteration_results and np.mod(float(i - 1), save_iteration_results) == 0: if "frd" in save_iteration_format: beso_lib.export_frd(path_export + "/file" + str(), nodes, Elements, elm_states, number_of_states) if "inp" in save_iteration_format: beso_lib.export_inp(path_export + "/file" + str(), nodes, Elements, elm_states, number_of_states) i += 1 # iteration number print("\n----------- new iteration number %d ----------" % i) # check for number of violated elements if sum(FI_violated[i - 1]) > sum(FI_violated[0]) + FI_violated_tolerance: mass_goal_i = mass[i - 1] # use mass_new from previous iteration if i_violated == 0: i_violated = i check_tolerance = True elif mass[i - 1] <= mass_goal_ratio * mass_full: # goal volume achieved if not i_violated: i_violated = i # to start decaying check_tolerance = True try: mass_goal_i except NameError: msg = "ERROR: mass goal is lower than initial mass. Check mass_goal_ratio" beso_lib.write_to_log(file_name, msg + "\n") else: mass_goal_i = mass_goal_ratio * mass_full return False
def run1(file_name, sensitivity_number, weight_factor_node, M, weight_factor_distance, near_nodes, nodes, opt_domains): sensitivity_number_node = { } # hypothetical sensitivity number of each node for nn in nodes: if nn in M: sensitivity_number_node[nn] = 0 for en in M[nn]: sensitivity_number_node[ nn] += weight_factor_node[nn][en] * sensitivity_number[en] sensitivity_number_filtered = sensitivity_number.copy( ) # sensitivity number of each element after filtering for en in opt_domains: numerator = 0 denominator = 0 for nn in near_nodes[en]: try: numerator += weight_factor_distance[ (en, nn)] * sensitivity_number_node[nn] denominator += weight_factor_distance[(en, nn)] except KeyError: pass if denominator != 0: sensitivity_number_filtered[en] = numerator / denominator else: msg = "\nERROR: filter over nodes failed due to division by 0." \ "Some element CG has not a node in distance <= r_min.\n" print(msg) beso_lib.write_to_log(file_name, msg) filter_on_sensitivity = 0 return sensitivity_number return sensitivity_number_filtered
def prepare3_tetra_grid(file_name, cg, r_min, opt_domains): weight_factor3 = {} near_points = {} near_elm = {} grid = 1.0 * r_min # ranges of xyz cycles should be set according to preset coefficient # searching for near points of each element for en in opt_domains: # domain to take elements for filtering x_elm, y_elm, z_elm = cg[en] # set starting point of the cell # grid is the length of tetrahedral edge, which is also cell x size x_en_cell = x_elm - x_elm % grid # v = grid * np.sqrt(3) / 2 is the high of triangle, which is the half of cell y size v = grid * 0.8660 y_en_cell = y_elm - y_elm % (2 * v) # h = grid * np.sqrt(2 / 3.0) is the high of tetrahedron, which is the half of cell z size h = grid * 0.8165 z_en_cell = z_elm - z_elm % (2 * h) near_points[en] = [] # comparing distance in cells around element en for x_cell in [x_en_cell - grid, x_en_cell, x_en_cell + grid]: for y_cell in [y_en_cell - 2 * v, y_en_cell, y_en_cell + 2 * v]: for z_cell in [ z_en_cell - 2 * h, z_en_cell, z_en_cell + 2 * h ]: for [x, y, z] in [ [x_cell, y_cell, z_cell], [x_cell + grid / 2.0, y_cell + v, z_cell], [x_cell + grid / 2.0, y_cell + v / 3.0, z_cell + h], [x_cell, y_cell + 4 / 3.0 * v, z_cell + h] ]: # grid point coordinates distance = ((x_elm - x)**2 + (y_elm - y)**2 + (z_elm - z)**2)**0.5 if distance < r_min: weight_factor3[(en, (x, y, z))] = r_min - distance near_points[en].append((x, y, z)) try: near_elm[(x, y, z)].append(en) except KeyError: near_elm[(x, y, z)] = [en] # summarize histogram of near elements hist_near_points = list(np.zeros(10)) for en in near_points: if isinstance(near_points[en], int): le = 1 else: le = len(near_points[en]) if le >= len(hist_near_points): while len(hist_near_points) <= le: hist_near_points.append(0) hist_near_points[le] += 1 msg = "\nfilter over points statistics:\n" msg += "histogram - number of near points (list index) vs. number of elements (value)\n" msg += str(hist_near_points) + "\n" beso_lib.write_to_log(file_name, msg) return weight_factor3, near_elm, near_points
def write_log(self): # writing log file with settings msg = "\n" msg += "---------------------------------------------------\n" msg += ("file_name = %s\n" % file_name) msg += ("Start at " + time.ctime() + "\n\n") for dn in domain_optimized: msg += ("elset_name = %s\n" % dn) msg += ("domain_optimized = %s\n" % domain_optimized[dn]) msg += ("domain_density = %s\n" % domain_density[dn]) msg += ("domain_thickness = %s\n" % domain_thickness[dn]) msg += ("domain_FI = %s\n" % domain_FI[dn]) msg += ("domain_material = %s\n" % domain_material[dn]) msg += "\n" msg += ("mass_goal_ratio = %s\n" % mass_goal_ratio) msg += ("filter_list = %s\n" % filter_list) msg += ("r_min = %s\n" % r_min) msg += ("filter_on_sensitivity = %s\n" % filter_on_sensitivity) msg += ("cpu_cores = %s\n" % cpu_cores) msg += ("FI_violated_tolerance = %s\n" % FI_violated_tolerance) msg += ("decay_coefficient = %s\n" % decay_coefficient) msg += ("shells_as_composite = %s\n" % shells_as_composite) msg += ("reference_points = %s\n" % reference_points) msg += ("reference_value = %s\n" % reference_value) msg += ("mass_addition_ratio = %s\n" % mass_addition_ratio) msg += ("mass_removal_ratio = %s\n" % mass_removal_ratio) msg += ("ratio_type = %s\n" % ratio_type) msg += ("sensitivity_averaging = %s\n" % sensitivity_averaging) msg += ("iterations_limit = %s\n" % iterations_limit) msg += ("tolerance = %s\n" % tolerance) msg += ("save_iteration_results = %s\n" % save_iteration_results) msg += ("save_iteration_format = %s\n" % save_iteration_format) msg += "\n" beso_lib.write_to_log(file_name, msg) # writing log table header msg = "\n" msg += "domain order: \n" dorder = 0 for dn in domains_from_config: msg += str(dorder) + ") " + dn + "\n" dorder += 1 msg += "\niteration mass FI_violated_0)" for dno in range(len(domains_from_config) - 1): msg += (" " + str(dno + 1)).rjust(4, " ") + ")" if len(domains_from_config) > 1: msg += " all)" msg += " FI_mean _without_state0 FI_max 0)" for dno in range(len(domains_from_config) - 1): msg += str(dno + 1).rjust(17, " ") + ")" if len(domains_from_config) > 1: msg += "all".rjust(17, " ") + ")" msg += "\n" beso_lib.write_to_log(file_name, msg)
def Update(self): ''' # computing mean stress from maximums of each element in all steps in the optimization domain ''' global i, FI_max, FI_mean, FI_mean_without_state0, FI_violated print("Update") FI_mean_sum = 0 FI_mean_sum_without_state0 = 0 mass_without_state0 = 0 for dn in domain_optimized: if domain_optimized[dn] is True: for en in domain_shells[dn]: mass_elm = domain_density[dn][elm_states[en]] * area_elm[ en] * domain_thickness[dn][elm_states[en]] FI_mean_sum += FI_step_max[en] * mass_elm if elm_states[en] != 0: FI_mean_sum_without_state0 += FI_step_max[en] * mass_elm mass_without_state0 += mass_elm for en in domain_volumes[dn]: mass_elm = domain_density[dn][ elm_states[en]] * volume_elm[en] FI_mean_sum += FI_step_max[en] * mass_elm if elm_states[en] != 0: FI_mean_sum_without_state0 += FI_step_max[en] * mass_elm mass_without_state0 += mass_elm FI_mean.append(FI_mean_sum / mass[i]) FI_mean_without_state0.append(FI_mean_sum_without_state0 / mass_without_state0) print("FI_mean = {}".format(FI_mean[i])) print("FI_mean_without_state0 = {}".format(FI_mean_without_state0[i])) # writing log table row msg = str().rjust(9, " ") + " " + str(mass[i]).rjust( 17, " ") + " " + str(FI_violated[i][0]).rjust(13, " ") for dno in range(len(domains_from_config) - 1): msg += " " + str(FI_violated[i][dno + 1]).rjust(4, " ") if len(domains_from_config) > 1: msg += " " + str(sum(FI_violated[i])).rjust(4, " ") msg += " " + str(FI_mean[i]).rjust(17, " ") + " " + str( FI_mean_without_state0[i]).rjust(18, " ") FI_max_all = 0 for dn in domains_from_config: msg += " " + str(FI_max[i][dn]).rjust(17, " ") FI_max_all = max(FI_max_all, FI_max[i][dn]) if len(domains_from_config) > 1: msg += " " + str(FI_max_all).rjust(17, " ") msg += "\n" beso_lib.write_to_log(file_name, msg)
def check_same_state(domain_same_state, filtered_dn, file_name): wrong_domains = False filtered_dn_set = set(filtered_dn) domains_to_check = set() for dn in domain_same_state: if domain_same_state[dn] in ["max", "average"]: domains_to_check.add(dn) if domains_to_check.intersection(filtered_dn_set): wrong_domains = True if wrong_domains is True: msg = "\nERROR: Filtering is used on domain with prescribed same state. It is recommended to exclude this domain" \ " from filtering.\n" beso_lib.write_to_log(file_name, msg) print(msg)
def run2(file_name, sensitivity_number, weight_factor2, near_elm, opt_domains): sensitivity_number_filtered = sensitivity_number.copy( ) # sensitivity number of each element after filtering for en in opt_domains: numerator = 0 denominator = 0 for en2 in near_elm[en]: ee = (min(en, en2), max(en, en2)) numerator += weight_factor2[ee] * sensitivity_number[en2] denominator += weight_factor2[ee] if denominator != 0: sensitivity_number_filtered[en] = numerator / denominator else: msg = "\nERROR: simple filter failed due to division by 0." \ "Some element has not a near element in distance <= r_min.\n" print(msg) beso_lib.write_to_log(file_name, msg) filter_on_sensitivity = 0 return sensitivity_number return sensitivity_number_filtered
msg += ("reference_value = %s\n" % reference_value) msg += ("mass_addition_ratio = %s\n" % mass_addition_ratio) msg += ("mass_removal_ratio = %s\n" % mass_removal_ratio) msg += ("ratio_type = %s\n" % ratio_type) msg += ("compensate_state_filter = %s\n" % compensate_state_filter) msg += ("sensitivity_averaging = %s\n" % sensitivity_averaging) msg += ("steps_superposition = %s\n" % steps_superposition) msg += ("iterations_limit = %s\n" % iterations_limit) msg += ("tolerance = %s\n" % tolerance) msg += ("displacement_graph = %s\n" % displacement_graph) msg += ("save_iteration_results = %s\n" % save_iteration_results) msg += ("save_solver_files = %s\n" % save_solver_files) msg += ("save_resulting_format = %s\n" % save_resulting_format) msg += "\n" file_name = os.path.join(path, file_name) beso_lib.write_to_log(file_name, msg) # mesh and domains importing [nodes, Elements, domains, opt_domains, en_all, plane_strain, plane_stress, axisymmetry] = beso_lib.import_inp( file_name, domains_from_config, domain_optimized, shells_as_composite) domain_shells = {} domain_volumes = {} for dn in domains_from_config: # distinguishing shell elements and volume elements domain_shells[dn] = set(domains[dn]).intersection(list(Elements.tria3.keys()) + list(Elements.tria6.keys()) + list(Elements.quad4.keys()) + list(Elements.quad8.keys())) domain_volumes[dn] = set(domains[dn]).intersection(list(Elements.tetra4.keys()) + list(Elements.tetra10.keys()) + list(Elements.hexa8.keys()) + list(Elements.hexa20.keys()) + list(Elements.penta6.keys()) + list(Elements.penta15.keys())) # initialize element states elm_states = {}
msg += ("reference_value = %s\n" % reference_value) msg += ("mass_addition_ratio = %s\n" % mass_addition_ratio) msg += ("mass_removal_ratio = %s\n" % mass_removal_ratio) msg += ("ratio_type = %s\n" % ratio_type) msg += ("compensate_state_filter = %s\n" % compensate_state_filter) msg += ("sensitivity_averaging = %s\n" % sensitivity_averaging) msg += ("steps_superposition = %s\n" % steps_superposition) msg += ("iterations_limit = %s\n" % iterations_limit) msg += ("tolerance = %s\n" % tolerance) msg += ("displacement_graph = %s\n" % displacement_graph) msg += ("save_iteration_results = %s\n" % save_iteration_results) msg += ("save_solver_files = %s\n" % save_solver_files) msg += ("save_resulting_format = %s\n" % save_resulting_format) msg += "\n" file_name = os.path.join(path, file_name) beso_lib.write_to_log(file_name, msg) if optimization_base == "stiffness" and reference_points == "nodes": msg = "reference_points set to 'nodes' is not supported for optimization_base as 'stiffness'" beso_lib.write_to_log(file_name, "\nERROR: " + msg + "\n") assert False, msg # mesh and domains importing [nodes, Elements, domains, opt_domains, en_all, plane_strain, plane_stress, axisymmetry] = beso_lib.import_inp( file_name, domains_from_config, domain_optimized, shells_as_composite) domain_shells = {} domain_volumes = {} for dn in domains_from_config: # distinguishing shell elements and volume elements domain_shells[dn] = set(domains[dn]).intersection(list(Elements.tria3.keys()) + list(Elements.tria6.keys()) + list(Elements.quad4.keys()) + list(Elements.quad8.keys())) domain_volumes[dn] = set(domains[dn]).intersection(list(Elements.tetra4.keys()) + list(Elements.tetra10.keys()) +
def prepare3_ortho_grid(file_name, cg, cg_min, r_min, opt_domains): weight_factor3 = {} near_points = {} near_elm = {} grid_size = 0.7 * r_min # constant less than sqrt(6)/2 is chosen ensuring that each element has at least 3 near points # if codes below are done for situation where grid_size > 0.5 * r_min # searching for near points of each element for en in opt_domains: # domain to take elements for filtering x_elm, y_elm, z_elm = cg[en] # set proper starting point coordinates reminder = divmod(x_elm - cg_min[0], grid_size)[1] if (grid_size + reminder) < r_min: x = x_elm - grid_size - reminder else: x = x_elm - reminder reminder = divmod(y_elm - cg_min[1], grid_size)[1] if (grid_size + reminder) < r_min: yy = y_elm - grid_size - reminder else: yy = y_elm - reminder reminder = divmod(z_elm - cg_min[2], grid_size)[1] if (grid_size + reminder) < r_min: zz = z_elm - grid_size - reminder else: zz = z_elm - reminder near_points[en] = [] # through points in the cube around element centre of gravity while x < x_elm + r_min: y = yy while y < y_elm + r_min: z = zz while z < z_elm + r_min: distance = ((x_elm - x)**2 + (y_elm - y)**2 + (z_elm - z)**2)**0.5 if distance < r_min: weight_factor3[(en, (x, y, z))] = r_min - distance near_points[en].append((x, y, z)) try: near_elm[(x, y, z)].append(en) except KeyError: near_elm[(x, y, z)] = [en] z += grid_size y += grid_size x += grid_size hist_near_elm = list(np.zeros(25)) hist_near_points = list(np.zeros(25)) for pn in near_elm: if isinstance(near_elm[pn], int): le = 1 else: le = len(near_elm[pn]) if le >= len(hist_near_elm): while len(hist_near_elm) <= le: hist_near_elm.append(0) hist_near_elm[le] += 1 for en in near_points: if isinstance(near_points[en], int): le = 1 else: le = len(near_points[en]) if le >= len(hist_near_points): while len(hist_near_points) <= le: hist_near_points.append(0) hist_near_points[le] += 1 msg = "\nfilter3 statistics:\n" msg += "histogram - number of near elements (list index) vs. number of points (value)\n" msg += str(hist_near_elm) + "\n" msg += "histogram - number of near points (list index) vs. number of elements (value)\n" msg += str(hist_near_points) + "\n" beso_lib.write_to_log(file_name, msg) return weight_factor3, near_elm, near_points
def Swith_Element_State(self): ''' # switch element states ''' global i, mass_referential, elm_states, mass, elm_states_before_last, elm_states_last, continue_iterations if ratio_type == "absolute": mass_referential = mass_full elif ratio_type == "relative": mass_referential = mass[i - 1] [elm_states, mass] = beso_lib.switching( elm_states, domains_from_config, domain_optimized, domains, FI_step_max, domain_density, domain_thickness, domain_shells, area_elm, volume_elm, sensitivity_number, mass, mass_referential, mass_addition_ratio, mass_removal_ratio, decay_coefficient, FI_violated, i_violated, i, mass_goal_i) # check for oscillation state if elm_states_before_last == elm_states: # oscillating state msg = "OSCILLATION: model turns back to " + str( i - 2) + "th iteration\n" beso_lib.write_to_log(file_name, msg) print(msg) continue_iterations = False elm_states_before_last = elm_states_last.copy() elm_states_last = elm_states.copy() # filtering state for ft in filter_list: if ft[0] and ft[1]: if len(ft) == 2: domains_to_filter = list(opt_domains) else: domains_to_filter = [] for dn in ft[2:]: domains_to_filter += domains[dn] if ft[0].split()[0] in [ "erode", "dilate", "open", "close", "open-close", "close-open", "combine" ]: if ft[0].split()[1] == "state": # the same filter as for sensitivity numbers elm_states_filtered = beso_filters.run_morphology( elm_states, near_elm, opt_domains, ft[0].split()[0]) # compute mass difference for dn in domains_from_config: if domain_optimized[dn] is True: for en in domain_shells[dn]: if elm_states[en] != elm_states_filtered[ en]: mass[i] += area_elm[en] * ( domain_density[dn][ elm_states_filtered[en]] * domain_thickness[dn][ elm_states_filtered[en]] - domain_density[dn][elm_states[en]] * domain_thickness[dn][ elm_states[en]]) elm_states[en] = elm_states_filtered[ en] for en in domain_volumes[dn]: if elm_states[en] != elm_states_filtered[ en]: mass[i] += volume_elm[en] * ( domain_density[dn][ elm_states_filtered[en]] - domain_density[dn][elm_states[en]]) elm_states[en] = elm_states_filtered[ en] print("mass = {}".format(mass[i])) print("Swith_Element_State")
def FEA(self): global i, FI_step, sensitivity_number, FI_violated, FI_step_max print("FEA") # creating a new .inp file for CalculiX file_nameW = path_export + '/' + str(i).zfill(3) beso_lib.write_inp(file_name, file_nameW, elm_states, number_of_states, domains, domains_from_config, domain_optimized, domain_thickness, domain_offset, domain_material, domain_volumes, domain_shells, plane_strain, plane_stress, axisymmetry, save_iteration_results, i, reference_points, shells_as_composite) # running CalculiX analysis subprocess.call(os.path.normpath(path_calculix) + " " + os.path.join(path, file_nameW), shell=True) # reading results and computing failure indeces if reference_points == "integration points": # from .dat file FI_step = beso_lib.import_FI_int_pt(reference_value, file_nameW, domains, criteria, domain_FI, file_name, elm_states, domains_from_config) elif reference_points == "nodes": # from .frd file FI_step = beso_lib.import_FI_node(reference_value, file_nameW, domains, criteria, domain_FI, file_name, elm_states) os.remove(file_nameW + ".inp") if not save_iteration_results or np.mod(float(i - 1), save_iteration_results) != 0: os.remove(file_nameW + ".dat") os.remove(file_nameW + ".frd") os.remove(file_nameW + ".sta") os.remove(file_nameW + ".cvg") FI_max.append({}) for dn in domains_from_config: FI_max[i][dn] = 0 for en in domains[dn]: for sn in range(len(FI_step)): try: FI_step_en = list( filter(lambda a: a is not None, FI_step[sn][en])) # drop None FI FI_max[i][dn] = max(FI_max[i][dn], max(FI_step_en)) except ValueError: msg = "FI_max computing failed. Check if each domain contains at least one failure criterion." beso_lib.write_to_log(file_name, "ERROR: " + msg + "\n") raise Exception(msg) except KeyError: msg = "Some result values are missing. Check available disk space." beso_lib.write_to_log(file_name, "ERROR: " + msg + "\n") raise Exception(msg) print("domain ordered \nFI_max and number of violated elements") # handling with more steps FI_step_max = { } # maximal FI over all steps for each element in this iteration FI_violated.append([]) dno = 0 for dn in domains_from_config: FI_violated[i].append(0) for en in domains[dn]: FI_step_max[en] = 0 for sn in range(len(FI_step)): FI_step_en = list( filter(lambda a: a is not None, FI_step[sn][en])) # drop None FI FI_step_max[en] = max(FI_step_max[en], max(FI_step_en)) sensitivity_number[en] = FI_step_max[en] / domain_density[dn][ elm_states[en]] if FI_step_max[en] >= 1: FI_violated[i][dno] += 1 print( str(FI_max[i][dn]).rjust(15) + " " + str(FI_violated[i][dno]).rjust(4)) dno += 1