[docs]classProblem(FEAData):"""Initialises the Problem object. Parameters ---------- name : str, optional Uniqe identifier. If not provided it is automatically generated. Set a name if you want a more human-readable input file. model : :class:`compas_fea2.model.Model` Model object to analyse. author : str, optional The author of the Model, by default ``None``. This will be added to the input file and can be useful for future reference. describption : str, optional Brief description of the Problem, , by default ``None``. This will be added to the input file and can be useful for future reference. Attributes ---------- name : str Uniqe identifier. If not provided it is automatically generated. Set a name if you want a more human-readable input file. model : :class:`compas_fea2.model.Model` Model object to analyse. author : str The author of the Model. This will be added to the input file and can be useful for future reference. describption : str Brief description of the Problem. This will be added to the input file and can be useful for future reference. steps : list of :class:`compas_fea2.problem._Step` list of analysis steps in the order they are applied. path : str, :class:`pathlib.Path` Path to the analysis folder where all the files will be saved. results : :class:`compas_fea2.results.Results` Results object with the analyisis results. """def__init__(self,model,name=None,author=None,description=None,**kwargs):super(Problem,self).__init__(name=name,**kwargs)self.author=authorself.description=descriptionor'Problem for {}'.format(model.name)self._path=Noneself._model=modelself._steps=set()self._steps_order=[]self._results=None@propertydefmodel(self):returnself._model@propertydefsteps(self):returnself._steps@propertydefpath(self):returnself._path@path.setterdefpath(self,value):self._path=valueifisinstance(value,Path)elsePath(value)@propertydefresults(self):returnself._results# =========================================================================# Step methods# =========================================================================
[docs]deffind_step_by_name(self,name):# type: (str) -> _Step"""Find if there is a step with the given name in the problem. Parameters ---------- name : str Returns ------- :class:`compas_fea2.problem._Step` """forstepinself.steps:ifstep.name==name:returnstep
[docs]defis_step_in_problem(self,step,add=True):"""Check if a :class:`compas_fea2.problem._Step` is defined in the Problem. Parameters ---------- step : :class:`compas_fea2.problem._Step` The Step object to find. Returns ------- :class:`compas_fea2.problem._Step` Raises ------ ValueError if `step` is a string and the step is not defined in the problem TypeError `step` must be either an instance of a `compas_fea2` Step class or the name of a Step already defined in the Problem. """ifnotisinstance(step,_Step):raiseTypeError('{!r} is not a Step'.format(step))ifstepnotinself.steps:print('{!r} not found'.format(step))ifadd:step=self.add_step(step)print('{!r} added to the Problem'.format(step))returnstepreturnFalsereturnTrue
[docs]defadd_step(self,step)->_Step:# # type: (Step) -> Step"""Adds a :class:`compas_fea2.problem._Step` to the problem. The name of the Step must be unique Parameters ---------- Step : :class:`compas_fea2.problem._Step` The analysis step to add to the problem. Returns ------- :class:`compas_fea2.problem._Step` """ifisinstance(step,_Step):ifself.find_step_by_name(step):raiseValueError('There is already a step with the same name in the model.')self._steps.add(step)self._steps_order.append(step)step._problem=selfelse:raiseTypeError('You must provide a valid compas_fea2 Step object')returnstep
[docs]defadd_steps(self,steps):"""Adds multiple :class:`compas_fea2.problem._Step` objects to the problem. Parameters ---------- steps : list[:class:`compas_fea2.problem._Step`] List of steps objects in the order they will be applied. Returns ------- list[:class:`compas_fea2.problem._Step`] """return[self.add_step(step)forstepinsteps]
[docs]defdefine_steps_order(self,order):"""Defines the order in which the steps are applied during the analysis. Parameters ---------- order : list List contaning the names of the analysis steps in the order in which they are meant to be applied during the analysis. Returns ------- None Warning ------- Not implemented yet! """forstepinorder:ifnotisinstance(step,_Step):raiseTypeError('{} is not a step'.format(step))self._steps_order=order
[docs]defadd_linear_perturbation_step(self,lp_step,base_step):"""Add a linear perturbation step to a previously defined step. Note ---- Linear perturbartion steps do not change the history of the problem (hence following steps will not consider their effects). Parameters ---------- lp_step : obj :class:`compas_fea2.problem.LinearPerturbation` subclass instance base_step : str name of a previously defined step which will be used as starting conditions for the application of the linear perturbation step. """raiseNotImplementedError
[docs]defsummary(self):"""Prints a summary of the Structure object. Parameters ---------- None Returns ------- None """steps_data='\n'.join([f'{step.name}'forstepinself.steps])summary=f"""++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++compas_fea2 Problem: {self._name}++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++description: {self.description}author: {self.author}Steps (in order of application)-------------------------------{steps_data}"""print(summary)returnsummary
[docs]@timer(message='Finished writing input file in')defwrite_input_file(self,path):"""Writes the abaqus input file. Parameters ---------- path : str, :class:`pathlib.Path` Path to the folder where the input file is saved. In case the folder does not exist, one is created. Returns ------- None """self.path=pathifnotself.path.exists():self.path.mkdir()input_file=InputFile.from_problem(self)input_file.write_to_file(self.path)
[docs]defanalyse(self,*args,**kwargs):raiseNotImplementedError("this function is not available for the selected backend")
[docs]@timer(message='Finished analysis and data extraction in')defanalyse_and_extract(self,fields=None,*args,**kwargs):"""Run the analysis through the registered backend. Parameters ---------- fields : list Output fields to extract, by default 'None'. If `None` all available fields will be extracted, which might require considerable time. Returns ------- None """importjsonfromcompas_fea2.resultsimportStepResultsself.analyse(*args,**kwargs)self.get_results()results_file=self._results.extract_data(fields)# Save results back into the Results objectwithopen(results_file,'rb')asf:results=json.load(f)forstepinresults:forpartinresults[step]:step_obj=self.find_step_by_name(step)ifnotstep_obj:raiseValueError('Step {} not found in the problem'.format(step))# TODO create a copy of the model for each stepstep_results=self._results.add_step_results(StepResults(step_obj,self.model))forresult_typein['nodes','elements']:ifresult_typeinresults[step][part]:items=getattr(step_results.model.find_part_by_name(part),result_type)forkey,fieldsinresults[step][part][result_type].items():item=[itemforiteminitemsifitem.key==int(key)][0]ifnothasattr(item,'results'):item.__setattr__('results',{})item.results[step]=fields
[docs]defshow(self,width=1600,height=900,scale_factor=1.,steps=None,parts=None,draw_original=True,draw_elements=True,draw_nodes=False,node_labels=False,draw_bcs=1.,draw_loads=None,**kwargs):fromcompas_fea2.UI.viewerimportFEA2Viewerfromcompas.geometryimportPoint,Vectorimportnumpyasnpfromcompas.colorsimportColorMap,Colorcmap=ColorMap.from_mpl('viridis')steps=stepsorself.stepsparts=partsorself.model.partsv=FEA2Viewer(width,height,scale_factor)ifdraw_original:v.draw_parts(parts,draw_elements,draw_nodes,node_labels)if'displacements'inkwargs:forstepinsteps:pts=[]vectors=[]lengths=[]forpartinparts:fornodeinpart.nodes:x,y,z=node.results[step.name]['U']vector=Vector(x=x,y=y,z=z)ifvector.length==0:continuevector.scale(kwargs['displacements'])vectors.append(vector)lengths.append(vector.length)pts.append(Point(*node.xyz))max_length=max([abs(length)forlengthinlengths])colors=[cmap(value/max_length)forvalueinlengths]v.draw_nodes_vector(pts,vectors,colors)# TODO create a copy of the model firstif'deformed'inkwargs:forstepinsteps:step_results=self.get_step_results(step)model=step_results.get_deformed_model(scale=kwargs['deformed'])deformed_parts=[model.find_part_by_name(part.name)forpartinparts]v.draw_parts(deformed_parts,draw_elements,draw_nodes,node_labels)ifdraw_bcs:v.draw_bcs(self.model,parts,draw_bcs)ifdraw_loads:v.draw_loads(steps,draw_loads)if'nforces'inkwargs:forstepinsteps:pts=[]vectors=[]lengths=[]forpartinparts:fornodeinpart.nodes:vector=Vector(x=node.results[step.name]['NFORC1'][0],y=node.results[step.name]['NFORC2'][0],z=node.results[step.name]['NFORC3'][0])ifvector.length==0:continuevector.scale(kwargs['nforces'])vectors.append(vector)lengths.append(vector.length)pts.append(Point(*node.xyz))max_length=max([abs(length)forlengthinlengths])colors=[cmap(value/max_length)forvalueinlengths]v.draw_nodes_vector(pts,vectors,colors)# TODO add reaction moments RMif'reactions'inkwargs:forstepinsteps:pts=[]vectors=[]lengths=[]forpartinparts:fornodeinpart.nodes:x,y,z=node.results[step.name]['RF']vector=Vector(x=x,y=y,z=z)ifvector.length==0:continuevector.scale(kwargs['reactions'])vectors.append(vector)lengths.append(vector.length)pts.append(Point(*node.xyz))max_length=max([abs(length)forlengthinlengths])colors=[cmap(value/max_length)forvalueinlengths]v.draw_nodes_vector(pts,vectors,colors)if'interface_forces'inkwargs:forstepinsteps:step_results=self.get_step_results(step)pts=[]vectors=[]lengths=[]forinterfaceinstep_results.model.interfaces:forint_sidein['master','slave']:pt,vector=step_results.get_resultant_force_at_interface(interface,side=int_side)# for node, vector in node_force_dict.items():# vector.scale(kwargs['interface_forces'])vector.scale(kwargs['interface_forces'])vectors.append(vector)lengths.append(vector.length)pts.append(pt)max_length=max([abs(length)forlengthinlengths])colors=[cmap(value/max_length)forvalueinlengths]v.draw_nodes_vector(pts,vectors,colors)v.show()