#make a material mat1 = febio.MatDef(matid=1,mname='Material 1',mtype='neo-Hookean', elsets=['exmymzm','exmypzm','exmymzp','exmypzp','expymzm','expypzm','expymzp','expypzp'],attributes={'density': '1.0', 'E': '1000.0','v': '0.3'}) #add the material to the model model.addMaterial(mat1) #make the geometry section of the model model.addGeometry(mesh=mesh,mats=[mat1]) #define a loadcurve model.addLoadCurve(lc='1',lctype='linear',points=[0,0,1,1]) model.addLoadCurve(lc='2',lctype='linear',points=[1,0,2,1]) #initialize a boundary condition object boundary = febio.Boundary(steps=2) #add a fixed boundary condition to bottom z nodes boundary.addFixed(nset=mesh.nsets['nzm'],dof='xyz') #add a prescribed displace to top z nodes for step 1 boundary.addPrescribed(step=0,nset=mesh.nsets['nzp'],dof='z',lc='1',scale='0.1') #add a relative prescribed displacement to top z nodes for step 2 boundary.addPrescribed(step=1,nset=mesh.nsets['nzp'],dof='z',lc='2',scale='0.2',ptype='relative') #add boundary conditions to model model.addBoundary(boundary=boundary) #create a control block ctrl = febio.Control() #add the control block to the model step 1 model.addControl(step=0,ctrl=ctrl)
class Model(): def __init__(self, **kwargs): self.options = FixedDict({ "Micro Model Base Name": "cell_scale", "Macro Model Geometry File": "tissue.vtr", "Macro Model Plot File": "tissue.xplt", "Shape Parameters": FixedDict({ "PCM Radius": [6.976, 3.9763, 2.9175], "Cell Radius": [6.0, 3.0, 2.0], "PCM Squareness": [2.1, 2.1, 2.1], "Cell Squareness": [2.1, 2.1, 2.1] }), "Mesh Divisions": FixedDict({ "Cell": 4.0, "PCM": 6.0, "ECM": 5.0 }), "Symmetry": "full", "Cell Shifts": { "neutral": 0.0 }, "Model Centroids": [[50, 50, 490]], "Material Properties": FixedDict({ "PCM E ratio": 0.2, "PCM v ratio": 1.0, "PCM beta ratio": 1.0, "PCM phi": 0.2, "PCM perm ratio": 1.0, "PCM m": 4.638, "PCM alpha": 0.0848, "Cell Modulus": 0.001, "Cell Poisson": 0.01, "Cell Permeability": 1e-3, "Cell phi": 0.1, "Membrane Modulus": 0.001, "Membrane Poisson": 0.0, "Membrane Permeability": 7e-10, "Membrane phi": 0.1, "Membrane Thickness": 1e-5 }) }) self.config = None for key, value in kwargs.items(): if key == "options": for k, v in value.items(): for k2, v2 in v.items(): self.options[k][k2] = v2 else: setattr(self, key, value) if self.config is not None: self.parseConfig() self.ecm_materials = {} self.pcm_materials = {} print( "... Extracting Macro Scale Results and Geometry from \n\t {} and {}" .format(self.options["Macro Model Plot File"], self.options["Macro Model Geometry File"])) self.assembleMacroData() print("... Extraction Complete") for case, shift in self.options["Cell Shifts"].items(): print("... Creating Mesh for {} case".format(case)) self.makeMesh(shift) print("... Mesh Completed") for translation in self.options["Model Centroids"]: print( "... ... Creating FEBio model for {} case at location: {:6.3f}, {:6.3f}, {:6.3f}" .format(case, *translation)) self.make_febio_model(case, translation) print("... ... Model written to {}_{}_{:d}.feb".format( self.options["Micro Model Base Name"], case, int(translation[2]))) def parseConfig(self): """ Parse configuration file in YAML format. """ with open(self.config) as user: user_settings = yaml.safe_load(user) for k, v in list(user_settings.items()): if isinstance(v, dict): for k2, v2 in list(v.items()): self.options[str(k)][str(k2)] = v2 else: self.options[str(k)] = v def assembleMacroData(self): # read macro_model from vtk grid reader = vtk.vtkXMLRectilinearGridReader() reader.SetFileName(self.options["Macro Model Geometry File"]) reader.Update() self.macro_data = reader.GetOutput() # parse FEBio xplt self.macro_results = febio.FebPlt( self.options["Macro Model Plot File"]) def makeMesh(self, cell_shift): resolution = 500 # Image resolution for image-based mesh cell_diameter = [ r * 2 for r in self.options["Shape Parameters"]["Cell Radius"] ] pcm_diameter = [ r * 2 for r in self.options["Shape Parameters"]["PCM Radius"] ] ecm_diameter = [d * 10.0**(1. / 3.) for d in pcm_diameter] cell_exponent = list( self.options["Shape Parameters"]["Cell Squareness"]) pcm_exponent = list(self.options["Shape Parameters"]["PCM Squareness"]) ecm_exponent = pcm_exponent symmetry = self.options["Symmetry"] if symmetry == 'full': position = [0.5, 0.5, 0.5] cell_position = [0.5, 0.5, 0.5 + cell_shift / ecm_diameter[2]] elif symmetry == 'half': position = [0.0, 0.5, 0.5] cell_position = [0.0, 0.5, 0.5 + cell_shift / ecm_diameter[2]] elif symmetry == 'quarter': position = [0.0, 0.0, 0.5] cell_position = [0.0, 0.0, 0.5 + cell_shift / ecm_diameter[2]] else: raise KeyError( "Symmetry must be set to 1 of the following: full, half, quarter" ) dmax = np.max(ecm_diameter) resolution = [int(d3 / dmax * resolution) for d3 in ecm_diameter] semisizes1 = [ 0.5 * d1 / d3 for d1, d3 in zip(cell_diameter, ecm_diameter) ] semisizes2 = [ 0.5 * d2 / d3 for d2, d3 in zip(pcm_diameter, ecm_diameter) ] s1 = raster_geometry.nd_superellipsoid(resolution, semisizes=semisizes1, indexes=cell_exponent, position=cell_position, smoothing=True) s2 = raster_geometry.nd_superellipsoid(resolution, semisizes=semisizes2, indexes=pcm_exponent, position=position, smoothing=True) s3 = raster_geometry.nd_superellipsoid(resolution, semisizes=[0.5, 0.5, 0.5], indexes=ecm_exponent, position=position, smoothing=True) s1 += 0.2 s2 += 0.2 s3 += 0.2 v = s1.astype(np.uint8) + s2.astype(np.uint8) + s3.astype(np.uint8) h = [r / res for res, r in zip(resolution, ecm_diameter)] divisions = self.options["Mesh Divisions"] cell_sizes_map = { 1: np.min(np.array(ecm_diameter) - np.array(pcm_diameter)) / divisions["ECM"] / 2.0, 2: np.min(np.array(pcm_diameter) - np.array(cell_diameter)) / divisions["PCM"] / 2.0, 3: np.min(np.array(cell_diameter)) / divisions["Cell"] / 2.0 } max_facet_distance = np.min(h) if symmetry != 'full': max_facet_distance /= 2.0 mesh = pygalmesh.generate_from_array( v, h, max_facet_distance=max_facet_distance, max_radius_surface_delaunay_ball=cell_sizes_map[1], max_circumradius_edge_ratio=1.3, max_cell_circumradius=cell_sizes_map, max_edge_size_at_feature_edges=cell_sizes_map[2], lloyd=True, odt=True, verbose=False) mesh.points = (mesh.points - np.array(ecm_diameter) / 2.0) / 1000. self.nodes = mesh.points # Get face connectivity and flatten - keep only unique node ids surface = np.sort(np.unique(mesh.get_cells_type('triangle').ravel())) # Get Domain IDs of all nodes node_type = mesh.point_data['medit:ref'] # Keep domain ID of only face nodes s_node_types = node_type[surface] # Define node set for outermost faces - domain ID = 1 self.surface_nodes = surface[s_node_types == 1] self.elements = mesh.get_cells_type('tetra') # get the domain ID of the solid elements element_data = mesh.get_cell_data('medit:ref', 'tetra') # Make sets from domain IDs self.cell_set = np.argwhere(element_data == 3) self.pcm_set = np.argwhere(element_data == 2) self.ecm_set = np.argwhere(element_data == 1) # Get Face connectivity list triangles = mesh.get_cells_type('triangle') # Get Domain ID of face elements surface_data = mesh.get_cell_data('medit:ref', 'triangle') # Define membrane elements - Domain ID = 3 self.membrane = triangles[surface_data == 3] # Define element set for membrane self.membrane_set = np.argwhere(surface_data == 3) def make_vtk(self, translation): vtk_mesh = vtk.vtkUnstructuredGrid() translated_nodes = self.nodes + np.array(translation) / 1000. nodearray = numpy_support.numpy_to_vtk(translated_nodes.ravel(), deep=True, array_type=vtk.VTK_DOUBLE) nodearray.SetNumberOfComponents(3) nodes = vtk.vtkPoints() nodes.SetData(nodearray) vtk_mesh.SetPoints(nodes) element_array = np.hstack([ np.ones((self.elements.shape[0], 1), dtype=int) * 4, self.elements ]) size = element_array.size element_array = numpy_support.numpy_to_vtk(element_array.ravel(), deep=True, array_type=vtk.VTK_ID_TYPE) elements = vtk.vtkCellArray() elements.SetCells(size // 5, element_array) vtk_mesh.SetCells(10, elements) element_set = self._interpMaterials(vtk_mesh) element_set[self.cell_set] = 1 element_set = numpy_support.numpy_to_vtk(element_set.ravel(), deep=True, array_type=vtk.VTK_CHAR) element_set.SetNumberOfComponents(1) element_set.SetName('Material ID') vtk_mesh.GetCellData().AddArray(element_set) return vtk_mesh def _interpMaterials(self, polydata): self.ecm_materials = {} self.pcm_materials = {} probe = vtk.vtkProbeFilter() probe.SetSourceData(self.macro_data) probe.SetInputData(polydata) probe.PassPointArraysOff() probe.PassCellArraysOn() probe.Update() to_cell = vtk.vtkPointDataToCellData() to_cell.SetInputData(probe.GetOutput()) to_cell.Update() mapped_data = to_cell.GetOutput() materialIDs = numpy_support.vtk_to_numpy( mapped_data.GetCellData().GetArray('ElementSetID')).astype(int) ksi = numpy_support.vtk_to_numpy( mapped_data.GetCellData().GetArray('ksi')) E = numpy_support.vtk_to_numpy(mapped_data.GetCellData().GetArray('E')) v = numpy_support.vtk_to_numpy(mapped_data.GetCellData().GetArray('v')) beta = numpy_support.vtk_to_numpy( mapped_data.GetCellData().GetArray('beta')) phi = numpy_support.vtk_to_numpy( mapped_data.GetCellData().GetArray('phi')) permeability = numpy_support.vtk_to_numpy( mapped_data.GetCellData().GetArray('permeability')) m = numpy_support.vtk_to_numpy(mapped_data.GetCellData().GetArray('m')) alpha = numpy_support.vtk_to_numpy( mapped_data.GetCellData().GetArray('alpha')) unique_materialIDs = np.sort(np.unique(materialIDs)) pcm_counter = 0 pcm_offset = unique_materialIDs.size for i, matid in enumerate(unique_materialIDs): idx = np.argwhere(materialIDs == matid) ecm_ids = np.intersect1d(idx.ravel(), self.ecm_set) pcm_ids = np.intersect1d(idx.ravel(), self.pcm_set) E_ratio = self.options['Material Properties']['PCM E ratio'] v_ratio = self.options['Material Properties']['PCM v ratio'] beta_ratio = self.options['Material Properties']['PCM beta ratio'] perm_ratio = self.options['Material Properties']['PCM perm ratio'] self.ecm_materials[i + 3] = { 'E': E[idx[0]][0], 'v': v[idx[0]][0], 'beta': beta[idx[0]][0], 'phi': phi[idx[0]][0], 'permeability': permeability[idx[0]][0], 'm': m[idx[0]][0], 'alpha': alpha[idx[0]][0], 'ksi': ksi[idx[0], :][0], 'element_ids': ecm_ids } materialIDs[ecm_ids] = i + 3 if pcm_ids.any(): self.pcm_materials[pcm_counter + 3 + pcm_offset] = { 'E': E[idx[0]][0] * E_ratio, 'v': v[idx[0]][0] * v_ratio, 'beta': beta[idx[0]][0] * beta_ratio, 'phi': phi[idx[0]][0], 'permeability': permeability[idx[0]][0] * perm_ratio, 'm': m[idx[0]][0], 'alpha': alpha[idx[0]][0], 'element_ids': pcm_ids } materialIDs[pcm_ids] = pcm_counter + 3 + pcm_offset pcm_counter += 1 return materialIDs def make_febio_model(self, case, translation): model_name = "{}_{}_{:d}".format(self.options["Micro Model Base Name"], case, int(translation[2])) fe_mesh = febio.MeshDef() for i in range(self.nodes.shape[0]): fe_mesh.nodes.append([i + 1] + list(self.nodes[i, :] + np.array(translation) / 1000.)) for i in range(self.elements.shape[0]): fe_mesh.elements.append(['tet4', i + 1] + list(self.elements[i, :] + 1)) membrane_set = np.zeros(self.membrane.shape[0], dtype=int) for i in range(self.membrane.shape[0]): fe_mesh.elements.append(['tri3', i + 1 + self.elements.shape[0]] + list(self.membrane[i] + 1)) membrane_set[i] = i + 1 + self.elements.shape[0] vtkmesh = self.make_vtk(translation) writer = vtk.vtkXMLUnstructuredGridWriter() writer.SetFileName(model_name + '.vtu') writer.SetInputData(vtkmesh) writer.Write() # Cell Element Set fe_mesh.addElementSet(setname='Cell', eids=self.cell_set + 1) # Membrane Element Set fe_mesh.addElementSet(setname='Membrane', eids=membrane_set) # PCM Element Set for k, v in self.pcm_materials.items(): fe_mesh.addElementSet(setname='PCM {:d}'.format(k), eids=v['element_ids'] + 1) # ECM Element Set for k, v in self.ecm_materials.items(): fe_mesh.addElementSet(setname='ECM {:d}'.format(k), eids=v['element_ids'] + 1) fe_model = febio.Model(modelfile='.'.join([model_name, 'feb']), steps=[{ 'Displace': 'biphasic' }]) mat_props = self.options["Material Properties"] # Cell Material cell_mat = febio.MatDef( matid=1, mname='Cell', mtype='biphasic', elsets='Cell', attributes={'phi0': '{:.6E}'.format(mat_props["Cell phi"])}) cell_mat.addBlock(branch=1, btype='solid', mtype='neo-Hookean', attributes={ "E": "{:.6E}".format(mat_props["Cell Modulus"]), "v": "{:.6E}".format(mat_props["Cell Poisson"]) }) cell_mat.addBlock(branch=1, btype='permeability', mtype='perm-const-iso', attributes={ "perm": "{:.6E}".format(mat_props["Cell Permeability"]) }) fe_model.addMaterial(cell_mat) # Membrane Material mem_mat = febio.MatDef( matid=2, mname='Membrane', mtype='biphasic', elsets='Membrane', attributes={"phi0": "{:.6E}".format(mat_props["Membrane phi"])}) mem_mat.addBlock(branch=1, btype='solid', mtype='neo-Hookean', attributes={ "E": "{:.6E}".format(mat_props["Membrane Modulus"]), "v": "{:.6E}".format(mat_props["Membrane Poisson"]) }) mem_mat.addBlock(branch=1, btype='permeability', mtype='perm-const-iso', attributes={ "perm": "{:.6E}".format( mat_props["Membrane Permeability"]) }) fe_mesh.addElementData(elset='Membrane', attributes={ "thickness": "{:.6E}".format( mat_props["Membrane Thickness"]) }) fe_model.addMaterial(mem_mat) # ECM Materials ecm_mats = [] for k, v in self.ecm_materials.items(): mname = 'ECM {:d}'.format(k) ecm_mats.append( febio.MatDef(matid=k, mname=mname, mtype='biphasic', elsets=mname, attributes={'phi0': '{:6E}'.format(v['phi'])})) ecm_mats[-1].addBlock( branch=1, btype='solid', mtype='solid mixture', attributes={'mat_axis': ['vector', "1,0,0", "0,1,0"]}) ecm_mats[-1].addBlock(branch=2, btype='solid', mtype='Holmes-Mow', attributes={ "E": "{:.6E}".format(v["E"]), "v": "{:.6E}".format(v["v"]), "beta": "{:.6E}".format(v["beta"]) }) ecm_mats[-1].addBlock( branch=2, btype='solid', mtype='ellipsoidal fiber distribution', attributes={ "ksi": "{:.6E}, {:.6E}, {:.6E}".format(v["ksi"][0], v["ksi"][1], v["ksi"][2]), "beta": "{:.6E}, {:.6E}, {:.6E}".format(2.0, 2.0, 2.0) }) ecm_mats[-1].addBlock(branch=1, btype='permeability', mtype='perm-Holmes-Mow', attributes={ "perm": "{:.6E}".format(v["permeability"]), "M": "{:.6E}".format(v["m"]), "alpha": "{:.6E}".format(v["alpha"]) }) fe_model.addMaterial(ecm_mats[-1]) # PCM Material pcm_mats = [] for k, v in self.pcm_materials.items(): mname = 'PCM {:d}'.format(k) pcm_mats.append( febio.MatDef(matid=k, mname=mname, mtype='biphasic', elsets=mname, attributes={'phi0': '{:6E}'.format(v['phi'])})) pcm_mats[-1].addBlock(branch=1, btype='solid', mtype='Holmes-Mow', attributes={ "E": "{:.6E}".format(v["E"]), "v": "{:.6E}".format(v["v"]), "beta": "{:.6E}".format(v["beta"]) }) pcm_mats[-1].addBlock(branch=1, btype='permeability', mtype='perm-Holmes-Mow', attributes={ "perm": "{:.6E}".format(v["permeability"]), "M": "{:.6E}".format(v["m"]), "alpha": "{:.6E}".format(v["alpha"]) }) fe_model.addMaterial(pcm_mats[-1]) fe_model.addGeometry(mesh=fe_mesh, mats=[cell_mat, mem_mat] + ecm_mats + pcm_mats) # Boundary Conditions fe_model.addLoadCurve(lc='1', lctype='linear', points=np.ravel( list( zip(self.macro_results.TIME, self.macro_results.TIME)))) boundary = febio.Boundary() nids = self.surface_nodes dx, dy, dz, p = self._interpBCs(vtkmesh) cnt = 2 for i in range(dx.shape[0]): fe_model.addLoadCurve( lc='{:d}'.format(cnt), lctype='smooth', points=np.ravel(list(zip(self.macro_results.TIME, dx[i, :])))) boundary.addPrescribed(nodeid=nids[i] + 1, scale=1.0, dof='x', lc='{:d}'.format(cnt)) cnt += 1 fe_model.addLoadCurve( lc='{:d}'.format(cnt), lctype='smooth', points=np.ravel(list(zip(self.macro_results.TIME, dy[i, :])))) boundary.addPrescribed(nodeid=nids[i] + 1, scale=1.0, dof='y', lc='{:d}'.format(cnt)) cnt += 1 fe_model.addLoadCurve( lc='{:d}'.format(cnt), lctype='smooth', points=np.ravel(list(zip(self.macro_results.TIME, dz[i, :])))) boundary.addPrescribed(nodeid=nids[i] + 1, scale=1.0, dof='z', lc='{:d}'.format(cnt)) cnt += 1 fe_model.addLoadCurve( lc='{:d}'.format(cnt), lctype='smooth', points=np.ravel(list(zip(self.macro_results.TIME, p[i, :])))) boundary.addPrescribed(nodeid=nids[i] + 1, scale=1.0, dof='p', lc='{:d}'.format(cnt)) cnt += 1 fe_model.addBoundary(boundary) ctrl = febio.Control() ctrl.setAttributes({ 'title': model_name, 'time_steps': '{:d}'.format( np.ceil(self.macro_results.TIME[-1] / self.macro_results.TIME[1]).astype(int)), 'step_size': '{:12.6f}'.format(self.macro_results.TIME[1]), 'time_stepper': { 'aggressiveness': '0', 'dtmin': '0.01', 'dtmax': 'lc=1', 'max_retries': '10', 'opt_iter': '10' }, 'max_ups': '0', 'max_refs': '10', 'dtol': '0.001', 'ptol': '0.01', 'lstol': '0.9', 'plot_level': 'PLOT_MUST_POINTS' }) fe_model.addControl(ctrl, step=0) fe_model.addOutput(output=[ "shell strain", "Lagrange strain", "effective shell fluid pressure", "relative volume" ]) fe_model.writeModel()
def performFEA(self): for filename in self.pickles: fid = open(filename, 'rb') self.data[filename] = pickle.load(fid) fid.close() mesh = febio.MeshDef() # would be good to vectorize these for i, e in enumerate(self.data[filename]['elements']): mesh.elements.append(['tet4', i + 1] + e) for i, n in enumerate(self.data[filename]['nodes']): mesh.nodes.append([i + 1] + n) mesh.addElementSet(setname='cell', eids=list(range(1, len(mesh.elements) + 1))) modelname = filename.replace('.pkl', '.feb') model = febio.Model(modelfile=modelname, steps=[{ 'Displace': 'solid' }]) #lookup table for proper FEBio keyword vs GUI text keywords = { "neoHookean": "neo-Hookean", "Young's Modulus": "E", "Poisson's Ratio": "v", "Mooney-Rivlin": "Mooney-Rivlin", "C_1": "c1", "C_2": "c2", "Bulk Modulus": "k", "Transversely Isotropic": "orthotropic elastic", "Young's Modulus (radial)": "E1", "Young's Modulus (tangential)": ["E2", "E3"], "Poisson's Ratio (radial:tangential)": "v12", "Poisson's Ratio (tangential:radial)": ["v23", "v31"], "Shear Modulus (radial planes)": ["G12", "G23", "G31"], "Continuous Fibre Distribution": "ellipsoidal fiber distribution" } #Ground Substance gtype = keywords[self.groundSubstances[self.groundSubstance.get() - 1]] gattributes = {} for (k, v) in list(self.groundParameters[self.groundSubstance.get() - 1].items()): # transversely isotropic case if isinstance(keywords[k], list): for a in keywords[k]: # G12 = E2 / (2 * (1 + v12)) if a == "G12": gattributes[a] = str( old_div(float(gattributes["E2"]), (2.0 * (1 + float(gattributes["v12"]))))) else: gattributes[a] = str(v.get()) # any other case else: gattributes[keywords[k]] = str(v.get()) if self.tensileNetwork.get() == 1: mat = febio.MatDef(matid=1, mname='cell', mtype=gtype, elsets='cell', attributes=gattributes) model.addMaterial(mat) model.addGeometry(mesh=mesh, mats=[mat]) #With a tensile network else: self.makeVTK(filename) self.findLocalCsys(filename) ind = self.tensileNetwork.get() - 1 ttype = keywords[self.tensileNetworks[ind]] ksi = [ self.tensileParameters[ind][k].get() for k in ("ksi1", "ksi2=ksi3", "ksi2=ksi3") ] beta = [ self.tensileParameters[ind][k].get() for k in ("beta1", "beta2=beta3", "beta2=beta3") ] mats = [] I1n2 = np.eye(3)[0:2, :] distances = numpy_support.vtk_to_numpy( self.vtkMeshes[filename].GetCellData().GetArray( "Signed Distances")) maxd = np.max(np.abs(distances)) for i, e in enumerate(self.data[filename]['elements']): normal = self.vtkMeshes[filename].GetCellData().GetArray( "LevelSet Normals").GetTuple3(i) # cross with both e1 and e2 to ensure linearly independent crossed = np.cross(normal, I1n2) norms = np.linalg.norm(crossed, axis=1) # get the index of the maximum norm; so never zero ind2 = np.argmax(norms) b = old_div(crossed[ind, :], norms[ind2]) normal = list(map(str, list(normal))) b = list(map(str, list(b))) d = self.tensileParameters[ind][ "Distance Cutoff [0, 1]"].get() k = self.tensileParameters[ind]["Cutoff Grade (0, 1]"].get( ) x = old_div(np.abs(distances[i]), maxd) w = 0.5 * (1 - np.tanh(old_div((x - d), k))) if w < 0.05: w = 0.05 tmp_ksi = [w * v for v in ksi] tattributes = { "ksi": ",".join(map(str, tmp_ksi)), "beta": ",".join(map(str, beta)) } mesh.addElementSet(setname='e{:d}'.format(i + 1), eids=[i + 1]) mats.append( febio.MatDef( matid=i + 1, mname='cell', mtype="solid mixture", elsets='e{:d}'.format(i + 1), attributes={ 'mat_axis': ['vector', ','.join(normal), ','.join(b)] })) mats[-1].addBlock(branch=1, btype='solid', mtype=gtype, attributes=gattributes) mats[-1].addBlock(branch=1, btype='solid', mtype=ttype, attributes=tattributes) model.addMaterial(mats[-1]) model.addGeometry(mesh=mesh, mats=mats) ctrl = febio.Control() ctrl.setAttributes({'title': 'cell', 'max_ups': '0'}) model.addControl(ctrl, step=0) boundary = febio.Boundary(steps=1) for i, bc in enumerate(self.data[filename]['boundary conditions']): boundary.addPrescribed( step=0, nodeid=self.data[filename]['surfaces'][i], dof='x', lc='1', scale=str(bc[0])) boundary.addPrescribed( step=0, nodeid=self.data[filename]['surfaces'][i], dof='y', lc='1', scale=str(bc[1])) boundary.addPrescribed( step=0, nodeid=self.data[filename]['surfaces'][i], dof='z', lc='1', scale=str(bc[2])) model.addBoundary(boundary=boundary) model.addLoadCurve(lc='1', lctype='linear', points=[0, 0, 1, 1]) model.writeModel() subprocess.call(self._FEBIO_BIN + " -i " + modelname, shell=True)