Source code for sfsimodels.models.buildings

from collections import OrderedDict

import numpy as np

from sfsimodels.models.abstract_models import PhysicalObject
from sfsimodels.models import SeismicHazard, Foundation, Soil
from sfsimodels.models.material import Concrete
from sfsimodels.exceptions import ModelError, deprecation
from sfsimodels import functions as sf


[docs]class Building(PhysicalObject): """ An object to define Buildings Parameters ---------- n_storeys : int Number of storeys """ _id = None name = None base_type = "building" type = "building" _floor_length = None _floor_width = None _interstorey_heights = np.array([0.0]) # m _storey_masses = np.array([0.0]) # kg _concrete = Concrete() _n_storeys = None _g = 9.81 # m/s2 # gravity def __init__(self, n_storeys, verbose=0, **kwargs): super(Building, self).__init__() self._n_storeys = n_storeys if not hasattr(self, "inputs"): self.inputs = [] self._extra_class_variables = [ "id", "name", "base_type", "type", 'n_storeys', 'floor_length', 'floor_width', 'interstorey_heights', 'storey_masses' ] self.inputs += self._extra_class_variables self.all_parameters = self.inputs + [ ] @property def ancestor_types(self): return super(Building, self).ancestor_types + ["building"] @property def id(self): return self._id @id.setter def id(self, value): self._id = value @property def floor_length(self): return self._floor_length @floor_length.setter def floor_length(self, value): self._floor_length = value @property def floor_width(self): return self._floor_width @floor_width.setter def floor_width(self, value): self._floor_width = value @property def g(self): return self._g @property def concrete(self): return self._concrete @concrete.setter def concrete(self, conc_inst): self._concrete = conc_inst @property def floor_area(self): try: return self.floor_length * self.floor_width except TypeError: return None @property def heights(self): return np.cumsum(self._interstorey_heights) @property def max_height(self): return np.sum(self._interstorey_heights) @property def n_storeys(self): return self._n_storeys @n_storeys.setter def n_storeys(self, value): assert self._n_storeys == value @property def interstorey_heights(self): return self._interstorey_heights @interstorey_heights.setter def interstorey_heights(self, heights): if len(heights) != self.n_storeys: raise ModelError("Specified heights must match number of storeys (%i)." % self.n_storeys) self._interstorey_heights = np.array(heights) @property def storey_masses(self): return self._storey_masses @storey_masses.setter def storey_masses(self, masses): self._storey_masses = np.array(masses)
[docs] def set_storey_masses_by_pressure(self, stresses): if hasattr(stresses, "length"): if len(stresses) != self.n_storeys: raise ModelError("Length of defined storey pressures: {0}, " "must equal number of stories: {1}".format(len(stresses), self.n_storeys)) self.storey_masses = stresses * self.floor_area / self._g else: self.storey_masses = stresses * np.ones(self.n_storeys) * self.floor_area / self._g
[docs]class Section(PhysicalObject): # not used? id = None type = "section" base_type = "section" _depth = None _width = None def __init__(self): self.inputs = ["depth", "width"] @property def depth(self): return self._depth @depth.setter def depth(self, value): self._depth = value @property def width(self): return self._width @width.setter def width(self, value): self._width = value
[docs]class Element(PhysicalObject): section_lengths = None def __init__(self, section_class=None): if section_class is None: self.sections = [Section()] else: self.sections = [section_class()] @property def s(self): return self.sections
[docs] def set_section_prop(self, prop, prop_value, sections=None): if sections is None: sections = range(len(self.sections)) for sect_i in sections: setattr(self.sections[sect_i], prop, prop_value)
[docs] def add_inputs_to_section(self, props, sections=None): if sections is None: sections = range(len(self.sections)) for sect_i in sections: self.sections[sect_i].inputs += props
[docs] def split_into_multiple(self, lengths): section = self.sections[0] self.section_lengths = [] self.sections = [] for length in lengths: self.section_lengths.append(length) self.sections.append(section.deepcopy())
[docs] def get_section_prop(self, prop, section_i=0): return getattr(self.sections[section_i], prop)
[docs] def to_dict(self, extra=(), **kwargs): output = [] for i in range(len(self.sections)): output.append(self.sections[i].to_dict(extra=extra)) return []
[docs]class Frame(object): _bay_lengths = None _custom_beam_section = None _custom_column_section = None _loaded_beam_section_ids = None # should not be accessed by end user _loaded_beam_sections = None # should not be accessed by end user _loaded_column_section_ids = None # should not be accessed by end user _loaded_column_sections = None # should not be accessed by end user def __init__(self, n_storeys, n_bays): if not hasattr(self, "inputs"): self.inputs = [] self._extra_class_variables = [ "n_bays", "n_storeys", "beams", "columns", "bay_lengths" ] self.inputs += self._extra_class_variables self._n_storeys = n_storeys self._n_bays = n_bays self._allocate_beams_and_columns()
[docs] def to_dict(self, extra=(), **kwargs): outputs = OrderedDict() skip_list = [] # skip_list = ["beams", "columns"] # TODO: uncomment this # skip_list = ["beams"] full_inputs = self.inputs + list(extra) for item in full_inputs: if item not in skip_list: value = self.__getattribute__(item) outputs[item] = sf.collect_serial_value(value) # # Deal with sections # beam_sections = OrderedDict() # beam_section_ids = [] # beam_section_count = 1 # for ss in range(self.n_storeys): # beam_section_ids.append([]) # for bb in range(self.n_bays): # beam_section_ids.append(beam_section_count) # # build a hash string of the section inputs to check uniqueness # parts = [] # for item in self.beams[ss][bb].inputs: # parts.append(item) # parts.append(getattr(self.beams[ss][bb], item)) # p_str = "-".join(parts) # if p_str not in beam_sections: # beam_sections[p_str] = self.beams[ss][bb].to_dict(extra) # beam_sections[p_str]["id"] = beam_section_count # beam_section_count += 1 # # outputs["beam_section_ids"] = beam_section_ids # outputs["beam_sections"] = OrderedDict() # for i, p_str in enumerate(beam_sections): # outputs["beam_sections"][beam_sections[p_str]["id"]] = beam_sections[p_str] return outputs
@property def ancestor_types(self): return ["frame"] def _allocate_beams_and_columns(self): self._beams = [[Element(self._custom_beam_section) for i in range(self.n_bays)] for ss in range(self.n_storeys)] self._columns = [[Element(self._custom_column_section) for i in range(self.n_cols)] for ss in range(self.n_storeys)] @property def beams(self): return self._beams @property def columns(self): return self._columns @property def n_bays(self): return self._n_bays @n_bays.setter def n_bays(self, value): assert self._n_bays == value @property def n_storeys(self): return self._n_storeys @n_storeys.setter def n_storeys(self, value): assert self._n_storeys == value @property def n_cols(self): return self._n_bays + 1 @property def beam_section_ids(self): return None @beam_section_ids.setter def beam_section_ids(self, beam_ids): self._loaded_beam_section_ids = beam_ids if self._loaded_beam_sections is not None: self._assign_loaded_beams() @property def beam_sections(self): return None @beam_sections.setter def beam_sections(self, beam_sections): self._loaded_beam_sections = beam_sections if self._loaded_beam_section_ids is not None: self._assign_loaded_beams() def _assign_loaded_beams(self): for ss in range(self.n_storeys): for bb in range(self.n_bays): sect_is = self._loaded_beam_section_ids[ss][bb] if hasattr(sect_is, "__len__"): n_sections = len(sect_is) self.beams[ss][bb].split_into_multiple([1] * n_sections) # TODO: should be lengths for sect_i in range(len(sect_is)): beam_sect_id = str(self._loaded_beam_section_ids[ss][bb][sect_i]) sect_dictionary = self._loaded_beam_sections[beam_sect_id] sf.add_to_obj(self.beams[ss][bb].sections[sect_i], sect_dictionary) else: # deprecated loading deprecation("Frame data structure is out-of-date, please load and save the file to update.") beam_sect_id = str(self._loaded_beam_section_ids[ss][bb]) sect_dictionary = self._loaded_beam_sections[beam_sect_id] sf.add_to_obj(self.beams[ss][bb].sections[0], sect_dictionary) @property def column_section_ids(self): return None @column_section_ids.setter def column_section_ids(self, column_ids): self._loaded_column_section_ids = column_ids if self._loaded_column_sections is not None: self._assign_loaded_columns() @property def column_sections(self): return None @column_sections.setter def column_sections(self, column_sections): self._loaded_column_sections = column_sections if self._loaded_column_section_ids is not None: self._assign_loaded_columns() def _assign_loaded_columns(self): for ss in range(self.n_storeys): for bb in range(self.n_bays): sect_is = self._loaded_column_section_ids[ss][bb] if hasattr(sect_is, "__len__"): n_sections = len(sect_is) self.columns[ss][bb].split_into_multiple([1] * n_sections) # TODO: should be lengths for sect_i in range(len(sect_is)): column_sect_id = str(self._loaded_column_section_ids[ss][bb][sect_i]) sect_dictionary = self._loaded_column_sections[column_sect_id] sf.add_to_obj(self.columns[ss][bb].sections[sect_i], sect_dictionary) else: # deprecated loading deprecation("Frame data structure is out-of-date, " "run sfsimodels.migrate_ecp(<file-path>, <out-file-path>).") column_sect_id = str(self._loaded_column_section_ids[ss][bb]) sect_dictionary = self._loaded_column_sections[column_sect_id] sf.add_to_obj(self.columns[ss][bb].sections[0], sect_dictionary) # @column_sections.setter # def column_sections(self, columns: dict): # assert len(columns) ==
[docs] def set_beam_prop(self, prop, values, repeat="up"): """ Specify the properties of the beams :param values: :param repeat: if 'up' then duplicate up the structure :return: """ values = np.array(values) if repeat == "up": assert len(values.shape) == 1 values = [values for ss in range(self.n_storeys)] else: assert len(values.shape) == 2 if len(values[0]) != self.n_bays: raise ModelError("beam depths does not match number of bays (%i)." % self.n_bays) for ss in range(self.n_storeys): for i in range(self.n_bays): self._beams[ss][i].set_section_prop(prop, values[0][i])
[docs] def set_column_prop(self, prop, values, repeat="up"): """ Specify the properties of the columns :param values: :param repeat: if 'up' then duplicate up the structure :return: """ values = np.array(values) if repeat == "up": assert len(values.shape) == 1 values = [values for ss in range(self.n_storeys)] else: assert len(values.shape) == 2 if len(values[0]) != self.n_cols: raise ModelError("column props does not match n_cols (%i)." % self.n_cols) for ss in range(self.n_storeys): for i in range(self.n_cols): self._columns[ss][i].set_section_prop(prop, values[0][i])
[docs] def beams_at_storey(self, storey): return self._beams[storey - 1]
@property def beam_depths(self): beam_depths = [] for ss in range(self.n_storeys): beam_depths.append([]) for i in range(self.n_bays): beam_depths[ss].append(self.beams[ss][i].get_section_prop("depth")) return np.array(beam_depths) @property def beam_widths(self): beam_widths = [] for ss in range(self.n_storeys): beam_widths.append([]) for i in range(self.n_bays): beam_widths[ss].append(self.beams[ss][i].get_section_prop("width")) return np.array(beam_widths) @property def column_depths(self): column_depths = [] for ss in range(self.n_storeys): column_depths.append([]) for i in range(self.n_cols): column_depths[ss].append(self.columns[ss][i].get_section_prop("depth")) return np.array(column_depths) @property def column_widths(self): column_widths = [] for ss in range(self.n_storeys): column_widths.append([]) for i in range(self.n_cols): column_widths[ss].append(self.columns[ss][i].get_section_prop("width")) return np.array(column_widths) @property def bay_lengths(self): return self._bay_lengths @bay_lengths.setter def bay_lengths(self, bay_lengths): if len(bay_lengths) != self.n_bays: raise ModelError("bay_lengths does not match number of bays (%i)." % self.n_bays) self._bay_lengths = np.array(bay_lengths)
[docs]class FrameBuilding(Frame, Building): _n_seismic_frames = None _n_gravity_frames = None type = "frame_building" def __init__(self, n_storeys, n_bays): Frame.__init__(self, n_storeys, n_bays) Building.__init__(self, n_storeys) # super(BuildingFrame, self).__init__(n_storeys, n_bays) # run parent class initialiser function self._extra_class_inputs = ["n_seismic_frames", "n_gravity_frames"] self.inputs = self.inputs + self._extra_class_inputs # Frame.__init__(self, n_storeys, n_bays) # Building.__init__(self, n_storeys, n_bays) @property def ancestor_types(self): return super(FrameBuilding, self).ancestor_types + ["frame_building"] @property def n_seismic_frames(self): return self._n_seismic_frames @n_seismic_frames.setter def n_seismic_frames(self, value): self._n_seismic_frames = value @property def n_gravity_frames(self): return self._n_gravity_frames @n_gravity_frames.setter def n_gravity_frames(self, value): self._n_gravity_frames = value
[docs]class BuildingFrame(FrameBuilding): def __init__(self, n_storeys, n_bays): deprecation("BuildingFrame is deprecated, use FrameBuilding.") super(BuildingFrame, self).__init__(n_storeys, n_bays)
[docs]class FrameBuilding2D(Frame, Building): _extra_class_inputs = [] type = "frame_building2D" def __init__(self, n_storeys, n_bays): Frame.__init__(self, n_storeys, n_bays) Building.__init__(self, n_storeys) # super(FrameBuilding2D, self).__init__(n_storeys, n_bays) # run parent class initialiser function self.inputs = self.inputs + self._extra_class_inputs @property def ancestor_types(self): return ["physical_object", "frame", "building"] + ["frame_building2D"] # TODO: improve this logic
[docs] def to_dict(self, extra=(), compression=True, **kwargs): outputs = OrderedDict() skip_list = ["beams", "columns"] full_inputs = self.inputs + list(extra) for item in full_inputs: if item not in skip_list: value = self.__getattribute__(item) outputs[item] = sf.collect_serial_value(value) # Deal with sections column_sections = OrderedDict() column_section_ids = [] column_section_count = 0 for ss in range(self.n_storeys): column_section_ids.append([]) for cc in range(self.n_cols): column_section_ids[ss].append([]) for sect_i in range(len(self.columns[ss][cc].sections)): if compression: # build a hash string of the section inputs to check uniqueness parts = [] for item in self.columns[ss][cc].sections[sect_i].inputs: if item == "id" or item == "name": continue parts.append(item) parts.append(str(self.columns[ss][cc].get_section_prop(item, section_i=sect_i))) p_str = "-".join(parts) else: p_str = str(sect_i) if p_str not in column_sections: column_sections[p_str] = self.columns[ss][cc].sections[sect_i].to_dict(extra) column_section_count += 1 col_sect_id = column_section_count column_sections[p_str]["id"] = col_sect_id else: col_sect_id = column_sections[p_str]["id"] column_section_ids[ss][cc].append(col_sect_id) beam_sections = OrderedDict() beam_section_ids = [] beam_section_count = 0 for ss in range(self.n_storeys): beam_section_ids.append([]) for bb in range(self.n_bays): beam_section_ids[ss].append([]) for sect_i in range(len(self.beams[ss][bb].sections)): if compression: # build a hash string of the section inputs to check uniqueness parts = [] for item in self.beams[ss][bb].sections[sect_i].inputs: if item == "id" or item == "name": continue parts.append(item) parts.append(str(self.beams[ss][bb].get_section_prop(item, section_i=sect_i))) p_str = "-".join(parts) else: p_str = str(sect_i) if p_str not in beam_sections: beam_sections[p_str] = self.beams[ss][bb].sections[sect_i].to_dict(extra) beam_section_count += 1 beam_sect_id = beam_section_count beam_sections[p_str]["id"] = beam_sect_id else: beam_sect_id = beam_sections[p_str]["id"] beam_section_ids[ss][bb].append(beam_sect_id) outputs["column_section_ids"] = column_section_ids outputs["beam_section_ids"] = beam_section_ids outputs["column_sections"] = OrderedDict() outputs["beam_sections"] = OrderedDict() for i, p_str in enumerate(column_sections): outputs["column_sections"][column_sections[p_str]["id"]] = column_sections[p_str] for i, p_str in enumerate(beam_sections): outputs["beam_sections"][beam_sections[p_str]["id"]] = beam_sections[p_str] return outputs
[docs]class BuildingFrame2D(FrameBuilding2D): def __init__(self, n_storeys, n_bays): deprecation("BuildingFrame2D class is deprecated, use FrameBuilding2D.") super(BuildingFrame2D, self).__init__(n_storeys, n_bays)
[docs]class WallBuilding(Building): # new name n_walls = 1 wall_depth = 0.0 # m wall_width = 0.0 # m type = "wall_building" def __init__(self, n_storeys): super(WallBuilding, self).__init__(n_storeys) # run parent class initialiser function self._extra_class_inputs = [ "n_walls", "wall_depth", "wall_width" ] self.inputs += self._extra_class_inputs @property def ancestor_types(self): return super(WallBuilding, self).ancestor_types + ["wall_building"]
[docs]class BuildingWall(WallBuilding): def __init__(self, n_storeys): deprecation("BuildingWall class is deprecated, use WallBuilding.") super(BuildingWall, self).__init__(n_storeys)
[docs]class SDOFBuilding(PhysicalObject): """ An object to describe structures. """ _id = None name = None base_type = "building" type = "sdof" _h_eff = None _mass_eff = None _t_fixed = None _mass_ratio = None def __init__(self, g=9.8): self.inputs = [ "id", "name", "base_type", "type", "h_eff", "mass_eff", "t_fixed", "mass_ratio" ] self._g = g @property def ancestor_types(self): return super(SDOFBuilding, self).ancestor_types + ["sdof"] @property def id(self): return self._id @id.setter def id(self, value): self._id = int(value) @property def h_eff(self): return self._h_eff @h_eff.setter def h_eff(self, value): if value is None or value == "": return self._h_eff = float(value) @property def mass_eff(self): return self._mass_eff @mass_eff.setter def mass_eff(self, value): if value is None or value == "": return self._mass_eff = float(value) @property def t_fixed(self): return self._t_fixed @t_fixed.setter def t_fixed(self, value): if value is None or value == "": return self._t_fixed = float(value) @property def mass_ratio(self): return self._mass_ratio @mass_ratio.setter def mass_ratio(self, value): if value is None or value == "": return self._mass_ratio = float(value) @property def k_eff(self): return 4.0 * np.pi ** 2 * self.mass_eff / self.t_fixed ** 2 @property def weight(self): return self.mass_eff / self.mass_ratio * self._g
[docs]class BuildingSDOF(SDOFBuilding): def __init__(self, g=9.8): deprecation("BuildingSDOF class is deprecated, use SDOFBuilding.") super(BuildingSDOF, self).__init__(g=g)
[docs]class Structure(BuildingSDOF): def __init__(self, g=9.8): deprecation("Structure class is deprecated, use BuildingSDOF.") super(Structure, self).__init__(g=g)
[docs]class SoilStructureSystem(PhysicalObject): bd = SDOFBuilding() fd = Foundation() sp = Soil() hz = SeismicHazard() name = "Nameless" inputs = ["name"] + bd.inputs + fd.inputs + sp.inputs + hz.inputs
if __name__ == '__main__': print(BuildingFrame2D.__mro__) a = BuildingFrame2D(1, 2) print(a.n_bays)