Source code for sharpy.generators.modifystructure

import numpy as np
import sharpy.utils.generator_interface as generator_interface
import sharpy.utils.settings as settings


[docs]@generator_interface.generator class ModifyStructure(generator_interface.BaseGenerator): """ ``ModifyStructure`` generator. This generator allows the user to modify structural parameters at runtime. At the moment, changes to lumped masses are supported. For each lumped mass you want to change, set ``change_variable`` to ``lumped_mass``, and the ``variable_index`` and ``file_list`` as specified in :class:`~sharpy.generators.modifystructure.ChangeLumpedMass`. This generator is called at the start of each time step in ``DynamicCoupled``. """ generator_id = 'ModifyStructure' settings_types = dict() settings_default = dict() settings_description = dict() settings_options = dict() settings_types['change_variable'] = 'list(str)' settings_default['change_variable'] = None settings_description['change_variable'] = 'Structural variable to modify' settings_options['change_variable'] = ['lumped_mass'] settings_types['variable_index'] = 'list(int)' settings_default['variable_index'] = None settings_description['variable_index'] = 'List of indices of variables to change. ' \ 'For instance the 1st lumped mass would be ``[0]``' settings_types['file_list'] = 'list(str)' settings_default['file_list'] = None settings_description['file_list'] = 'File path for each variable containing the changing info, in the ' \ 'appropriate format. See each of the allowed variables for the correct format.' def __init__(self): self.settings = None self.num_changes = None # :int number of variables that are changed self.variables = [] # :list of changed variables objects self.control_objects = {} #: dictionary of changed variable name and its control object as value def initialise(self, in_dict, **kwargs): structure = kwargs['data'].structure self.settings = in_dict settings.to_custom_types(self.settings, self.settings_types, self.settings_default, no_ctype=True, options=self.settings_options) self.num_changes = len(self.settings['change_variable']) if 'lumped_mass' in self.settings['change_variable']: self.control_objects['lumped_mass'] = LumpedMassControl() lumped_mass_variables = [] for i in range(self.num_changes): var_type = self.settings['change_variable'][i] if var_type == 'lumped_mass': variable = ChangeLumpedMass(var_index=self.settings['variable_index'][i], file=self.settings['file_list'][i]) variable.initialise(structure) self.variables.append(variable) lumped_mass_variables.append(i) self.control_objects['lumped_mass'].append(i) else: raise NotImplementedError('Variable {:s} not yet coded to be modified in runtime'.format(var_type)) try: self.control_objects['lumped_mass'].set_unchanged_vars_to_zero(structure) except KeyError: pass def generate(self, params): data = params['data'] ts = data.ts structure = data.structure for variable in self.variables: variable(structure, ts) # should only be called once per time step try: self.control_objects['lumped_mass'].execute_change(structure) except KeyError: pass
# for future variables supported, have the control objects have the same signatures such that they may be # called in a loop
[docs]class ChangedVariable: """ Base class of a changed variable Attributes: name (str): Name of the changed variable variable_index (int): Index of the variable to change file (str): Name of the file containing the input data (.txt) original (np.ndarray): Original value of the desired value in the appropriate format current_value (np.ndarray): Running track of the current value of the desired variable """ def __init__(self, name, var_index, file): self.name = name self.variable_index = var_index self.file = file self.original = None self.target_value = None self.current_value = None # initially def initialise(self, structure): self.get_original(structure) self.load_file() self.current_value = self.original # initially def __call__(self, structure, ts): pass def get_original(self, structure): # should be overridden for the desired variable class. it should set self.original in the appropriate format pass def load_file(self): self.target_value = np.loadtxt(self.file)
[docs]class ChangeLumpedMass(ChangedVariable): """ Lumped Mass to be modified The arguments are parsed as items of the list in the settings for ``variable_index`` and ``file_list``. For those variables marked where ``change_variables = 'lumped_mass'``. The file should contain a time varying series with the following 10 columns: * Lumped mass * Lumped mass position in the material frame ``B`` (3 columns for ``xb``, ``yb`` and ``zb``) * Lumped mass inertia in the material frame ``B`` (6 columns for ``ixx``, ``iyy``, ``izz``, ``ixy``, ``ixz`` and ``iyz``. Not all 10 columns are necessary in the input file, missing columns are ignored and left unchanged. There should be one row per time step. If there are not enough entries for the number of time steps in the simulation, the changed variable value remains unchanged after all rows have been processed. Args: var_index (int): Index of lumped mass. NOT the lumped mass node. file (str): Path to file containing time history of the lumped mass. """ def __init__(self, var_index, file): super().__init__('lumped_mass', var_index=var_index, file=file) def __call__(self, structure, ts): try: # lumped masses get added (+=) at structure.lump_masses(), therefore the increment with respect to the # previous time step must be provided. This is such that this generator is backwards compatible with the # way lumped masses are assembled. delta = self.target_value[ts] - self.current_value except IndexError: # input file has less entries than the simulation time steps structure.lumped_mass[self.variable_index] = 0 structure.lumped_mass_position[self.variable_index, :] = np.zeros(3) structure.lumped_mass_inertia[self.variable_index, :, :] = np.zeros((3, 3)) else: structure.lumped_mass[self.variable_index] = delta[0] structure.lumped_mass_position[self.variable_index, :] = delta[1:4] ixx, iyy, izz, ixy, ixz, iyz = delta[-6:] inertia = np.block([[ixx, ixy, ixz], [ixy, iyy, iyz], [ixz, iyz, izz]]) structure.lumped_mass_inertia[self.variable_index, :, :] = inertia self.current_value += delta
[docs] def load_file(self): """Sets ``self.target_value`` by reading from the file. If the input does not have as many columns as needed (10), these get padded with the original value such that they are not changed at runtime. """ super().load_file() n_values = len(self.original) try: n_target_values = self.target_value.shape[1] # number of columns except IndexError: n_target_values = 1 if n_target_values != n_values: # if not enough column entries pad with original values self.target_value = np.column_stack((self.target_value, self.original[-(n_values - n_target_values):] * np.ones((self.target_value.shape[0], n_values - n_target_values) ) ))
def get_original(self, structure): m = structure.lumped_mass[self.variable_index] pos = structure.lumped_mass_position[self.variable_index, :] inertia = structure.lumped_mass_inertia[self.variable_index, :, :] self.original = np.hstack((m, pos, np.diag(inertia), inertia[0, 1], inertia[0, 2], inertia[1, 2]))
[docs]class LumpedMassControl: """Lumped Mass Control Class This class is instantiated when at least one lumped mass is modified. It allows control over unchanged lumped masses and calls the method to execute the change. Attributes: lumped_mass_variables (list): List of integers containing the indices of the variables to change. These indices refer to the order in which they are provided in the general settings for the generator. """ def __init__(self): self.lumped_mass_variables = []
[docs] def set_unchanged_vars_to_zero(self, structure): """ Sets the lumped masses variables of unchanged lumped masses to zero. This is to avoid the lumped mass changing during execution Args: structure (sharpy.structure.models.beam.Beam): SHARPy structure object """ for i_lumped_mass in range(len(structure.lumped_mass)): if i_lumped_mass not in self.lumped_mass_variables: structure.lumped_mass[i_lumped_mass] *= 0 structure.lumped_mass_position[i_lumped_mass] *= 0 structure.lumped_mass_inertia[i_lumped_mass] *= 0
[docs] @staticmethod def execute_change(structure): """Executes the change in the lumped masses. Called only once per time step when all the changed lumped mass variables have been processed. """ # called once all variables changed structure.lump_masses() structure.generate_fortran()
def append(self, i): self.lumped_mass_variables.append(i)