def test_beam_with_isotropic_mass(beam_model_line, isotropic_mass: Mass, fem_format, test_fem_mass_dir): bm = beam_model_line.get_by_name("Bm") fix_nodes = get_beam_end_nodes(bm) fs_fix = bm.parent.fem.add_set( FemSet("FixSet", fix_nodes, FemSet.TYPES.NSET)) bm.parent.fem.add_bc(Bc("FixBc", fs_fix, [1, 2, 3, 4, 5, 6])) end_nodes = get_beam_end_nodes(bm, 2) fs_mass = bm.parent.fem.add_set( FemSet("MassSet", end_nodes, FemSet.TYPES.NSET)) isotropic_mass.fem_set = fs_mass bm.parent.fem.add_mass(isotropic_mass) a = bm.parent.get_assembly() a.fem.add_step(StepEigen("StepEig", num_eigen_modes=10)) name = f"bm_wIsoMass_{fem_format}" try: res = beam_model_line.to_fem(name, fem_format, scratch_dir=test_fem_mass_dir, overwrite=True) except IncompatibleElements as e: logging.error(e) return if res is not None and res.eigen_mode_data is not None: for mode in res.eigen_mode_data.modes: print(mode)
def grab_mass(match): d = match.groupdict() nodeno = str_to_int(d["nodeno"]) mass_in = [ roundoff(d["m1"]), roundoff(d["m2"]), roundoff(d["m3"]), roundoff(d["m4"]), roundoff(d["m5"]), roundoff(d["m6"]), ] masses = [m for m in mass_in if m != 0.0] if checkEqual2(masses): mass_type = None masses = [masses[0]] if len(masses) > 0 else [0.0] else: mass_type = "anisotropic" no = fem.nodes.from_id(nodeno) fem_set = FemSet(f"m{nodeno}", [], "elset", metadata=dict(internal=True), parent=fem) mass = Mass(f"m{nodeno}", fem_set, masses, "mass", ptype=mass_type, parent=fem) elem = Elem(no.id, [no], "mass", fem_set, mass_props=mass, parent=fem) fem.elements.add(elem) fem_set.add_members([elem]) fem.sets.add(fem_set) return Mass(f"m{nodeno}", fem_set, masses, "mass", ptype=mass_type, parent=fem)
def beam_ex1(p1=(0, 0, 0), p2=(1.5, 0, 0), profile="IPE400"): """ :return: :rtype: ada.Assembly """ bm = Beam("MyBeam", p1, p2, profile, Material("S355")) a = Assembly("Test", user=User("krande")) / [Part("MyPart") / bm] h = 0.2 r = 0.02 normal = [0, 1, 0] xdir = [-1, 0, 0] # Polygon Extrusions origin = np.array([0.2, -0.1, -0.1]) points = [(0, 0), (0.1, 0), (0.05, 0.1)] bm.add_penetration(PrimExtrude("Poly1", points, h, normal, origin, xdir)) origin += np.array([0.2, 0, 0]) points = [(0, 0, r), (0.1, 0, r), (0.05, 0.1, r)] bm.add_penetration(PrimExtrude("Poly2", points, h, normal, origin, xdir)) origin += np.array([0.2, 0, 0]) points = [(0, 0, r), (0.1, 0, r), (0.1, 0.2, r), (0.0, 0.2, r)] bm.add_penetration(PrimExtrude("Poly3", points, h, normal, origin, xdir)) # Cylinder Extrude x = origin[0] + 0.2 bm.add_penetration(PrimCyl("cylinder", (x, -0.1, 0), (x, 0.1, 0), 0.1)) # Box Extrude x += 0.2 bm.add_penetration(PrimBox("box", (x, -0.1, -0.1), (x + 0.2, 0.1, 0.1))) # Create a FEM analysis of the beam as a cantilever subjected to gravity loads p = a.get_part("MyPart") create_beam_mesh(bm, p.fem, "shell") # Add a set containing ALL elements (necessary for Calculix loads). fs = p.fem.add_set(FemSet("Eall", [el for el in p.fem.elements], "elset")) step = a.fem.add_step( Step("gravity", "static", nl_geom=True, init_incr=100.0, total_time=100.0)) step.add_load(Load("grav", "gravity", -9.81 * 800, fem_set=fs)) fix_set = p.fem.add_set(FemSet("bc_nodes", get_beam_end_nodes(bm), "nset")) a.fem.add_bc(Bc("Fixed", fix_set, [1, 2, 3])) return a
def grab_constraint(master, data, fem: FEM) -> Constraint: m = str_to_int(master) m_set = FemSet(f"co{m}_m", [fem.nodes.from_id(m)], "nset") slaves = [] for d in data: s = str_to_int(d["slave"]) slaves.append(fem.nodes.from_id(s)) s_set = FemSet(f"co{m}_s", slaves, "nset") fem.add_set(m_set) fem.add_set(s_set) return Constraint(f"co{m}", Constraint.TYPES.COUPLING, m_set, s_set, parent=fem)
def get_mpc(mpc_values): m_set, s_set = zip(*mpc_values) mpc_name = mpc_type + "_mpc" mset = FemSet("mpc_" + mpc_type + "_m", m_set, "nset") sset = FemSet("mpc_" + mpc_type + "_s", s_set, "nset") return Constraint(mpc_name, "mpc", mset, sset, mpc_type=mpc_type, parent=fem)
def get_mpc(mpc_values): m_set, s_set = zip(*mpc_values) mpc_name = mpc_type + "_mpc" mset = FemSet("mpc_" + mpc_type + "_m", m_set, FemSet.TYPES.NSET) sset = FemSet("mpc_" + mpc_type + "_s", s_set, FemSet.TYPES.NSET) return Constraint(mpc_name, Constraint.TYPES.MPC, mset, sset, mpc_type=mpc_type, parent=fem)
def grab_constraint(master, data): m = str_to_int(master) m_set = FemSet(f"co{m}_m", [fem.nodes.from_id(m)], "nset") slaves = [] for d in data: s = str_to_int(d["slave"]) slaves.append(fem.nodes.from_id(s)) s_set = FemSet(f"co{m}_m", slaves, "nset") fem.add_set(m_set) fem.add_set(s_set) return Constraint(f"co{m}", "coupling", m_set, s_set, parent=fem)
def get_nodes_and_elements(gmsh_session, fem, fem_set_name="all_elements"): nodes = list(gmsh_session.model.mesh.getNodes(-1, -1)) # Get nodes fem._nodes = Nodes( [ Node( [roundoff(x) for x in gmsh_session.model.mesh.getNode(n)[0]], n, parent=fem, ) for n in nodes[0] ], parent=fem, ) # Get elements elemTypes, elemTags, elemNodeTags = gmsh_session.model.mesh.getElements( 2, -1) elements = [] for k, element_list in enumerate(elemTags): face, dim, morder, numv, parv, _ = gmsh_session.model.mesh.getElementProperties( elemTypes[k]) elem_type = gmsh_map[face] for j, eltag in enumerate(element_list): nodes = [] for i in range(numv): idtag = numv * j + i p1 = elemNodeTags[k][idtag] nodes.append(fem.nodes.from_id(p1)) el = Elem(eltag, nodes, elem_type, parent=fem) elements.append(el) fem._elements = FemElements(elements, fem_obj=fem) femset = FemSet(fem_set_name, elements, "elset") fem.sets.add(femset)
def add_bcs(self): from ada.fem import Bc, FemSet for i, bc_loc in enumerate([self.c1, self.c2, self.c3, self.c4]): fem_set_btn = FemSet(f"fix{i}", [], "nset") self.fem.add_set(fem_set_btn, p=bc_loc(self._origin[2])) self.fem.add_bc(Bc(f"bc_fix{i}", fem_set_btn, [1, 2, 3]))
def test_bm_eigenfrequency_shell(self): bm = test_bm() p = Part("MyPart") a = Assembly("MyAssembly") / [p / bm] create_beam_mesh(bm, p.fem, "shell") fix_set = p.fem.add_set(FemSet("bc_nodes", get_beam_end_nodes(bm), "nset")) a.fem.add_bc(Bc("Fixed", fix_set, [1, 2, 3, 4, 5, 6])) a.fem.add_step(Step("Eigen", "eigenfrequency")) a.to_fem("Cantilever_CA_EIG_sh", "abaqus", overwrite=True) a.to_fem("Cantilever_CA_EIG_sh", "sesam", overwrite=True) res = a.to_fem("Cantilever_CA_EIG_sh", "code_aster", overwrite=True, execute=True) f = h5py.File(res.results_file_path) modes = f.get("CHA/modes___DEPL") for mname, m in modes.items(): mode = m.attrs["NDT"] freq = m.attrs["PDT"] print(mode, freq)
def get_bc(match): d = match.groupdict() bc_name = d["name"] if d["name"] is not None else next(bc_counter) bc_type = d["type"] set_name, dofs, magn = get_dofs(d["content"]) if "." in set_name: part_instance_name, set_name = set_name.split(".") fem_set = get_nset(part_instance_name, set_name) else: if set_name in fem.nsets.keys(): fem_set = fem.sets.get_nset_from_name(set_name) else: val = str_to_int(set_name) if val in fem.nodes.dmap.keys(): node = fem.nodes.from_id(val) fem_set = FemSet(bc_name + "_set", [node], "nset", parent=fem) else: raise ValueError( f'Unable to find set "{set_name}" in part {fem}') if fem_set is None: raise Exception("Unable to Find node set") props = dict() if bc_type is not None: props["bc_type"] = bc_type if magn is not None: props["magnitudes"] = magn return Bc(bc_name, fem_set, dofs, parent=fem, **props)
def convert_springs_to_connectors(assembly): """ Converts all single noded springs to connector elements :param assembly: :type assembly: ada.Assembly """ import numpy as np from ada import Node from ada.fem import Bc, Connector, ConnectorSection, FemSet for p in assembly.get_all_subparts(): for spring in p.fem.springs.values(): n1 = spring.nodes[0] n2 = Node(n1.p - np.array([0, 0, 10e-3])) assembly.fem.add_rp(spring.name + "_rp", n2) fs = FemSet(spring.name + "_bc", [n2], "nset") assembly.fem.add_set(fs) assembly.fem.add_bc(Bc(spring.name + "_bc", fs, [1, 2, 3, 4, 5, 6])) diag = [] for dof, row in enumerate(spring.stiff): for j, stiffness in enumerate(row): if dof == j: diag.append(stiffness) con_sec = ConnectorSection(spring.name + "_consec", diag, []) assembly.fem.add_connector_section(con_sec) con = Connector(spring.name + "_con", spring.id, n1, n2, "bushing", con_sec) assembly.fem.add_connector(con) p.fem._springs = dict() p.fem.elements.filter_elements(delete_elem=["SPRING1"])
def grab_elements(match): d = match.groupdict() el_no = str_to_int(d["elno"]) el_nox = str_to_int(d["elnox"]) internal_external_element_map[el_no] = el_nox nodes = [ fem.nodes.from_id(x) for x in filter( lambda x: x != 0, map(str_to_int, d["nids"].replace("\n", "").split()), ) ] eltyp = d["eltyp"] el_type = sesam_eltype_2_general(str_to_int(eltyp)) if el_type in ("SPRING1", "SPRING2"): spring_elem[el_no] = dict(gelmnt=d) return None metadata = dict(eltyad=str_to_int(d["eltyad"]), eltyp=eltyp) elem = Elem(el_no, nodes, el_type, None, parent=fem, metadata=metadata) if el_type == Elem.EL_TYPES.MASS_SHAPES.MASS: logging.warning( "Mass element interpretation in sesam is undergoing changes. Results should be checked" ) mass_elem[el_no] = dict(gelmnt=d) fem.sets.add( FemSet(f"m{el_no}", [elem], FemSet.TYPES.ELSET, parent=fem)) return elem
def _build_sets_from_elsets(self, elset, elem_iter): """ :param elset: :param elem_iter: """ from ada.fem import FemSet if elset is not None and type(elset) == str: def link_elset(elem, elem_set_): elem._elset = elem_set_ elements = [self.from_id(el.id) for el in elem_iter] if elset not in self._fem_obj.elsets: elem_set = FemSet(elset, elements, "elset", parent=self._fem_obj) self._fem_obj.sets.add(elem_set) list(map(lambda x: link_elset(x, elem_set), elements)) else: self._fem_obj.elsets[elset].add_members(elements) elem_set = self._fem_obj.elsets[elset] list(map(lambda x: link_elset(x, elem_set), elements))
def get_set(match): name = match.group(2) set_type = match.group(1) internal = True if match.group(3) is not None else False instance = match.group(4) generate = True if match.group(5) is not None else False members_str = match.group(6) gen_mem = str_to_ints(members_str) if generate is True else [] members = [] if generate is True else str_to_ints(members_str) metadata = dict(instance=instance, internal=internal, generate=generate, gen_mem=gen_mem) parent_instance = get_parent_instance(instance) if set_type.lower() == "elset": members = [ parent_instance.elements.from_id(el_id) for el_id in members ] else: members = [ parent_instance.nodes.from_id(el_id) for el_id in members ] fem_set = FemSet( name, members, set_type=set_type, metadata=metadata, parent=parent_instance, ) return fem_set
def getnodes(m): d = m.groupdict() res = np.fromstring(list_cleanup(d["members"]), sep=",", dtype=np.float64) res_ = res.reshape(int(res.size / 4), 4) members = [Node(n[1:4], int(n[0]), parent=parent) for n in res_] if d["nset"] is not None: parent.sets.add(FemSet(d["nset"], members, "nset", parent=parent)) return members
def get_beam_elements(self, li, order): """ :param li: :param order: :return: """ import gmsh from ada.core.utils import make_name_fem_ready model = gmsh.model bm = self._bm_map[li] segments = model.mesh.getElements(1, li)[1][0] fem_nodes = model.mesh.getElements(1, li)[2][0] elem_types, elem_tags, elem_node_tags = gmsh.model.mesh.getElements( 1, li) face, dim, morder, numv, parv, _ = gmsh.model.mesh.getElementProperties( elem_types[0]) set_name = make_name_fem_ready(f"el{bm.name}_set") fem = self._part.fem def make_elem(j): no = [] for i in range(numv): p1 = fem_nodes[numv * j + i] p1_co = gmsh.model.mesh.getNode(p1)[0] no.append(Node(p1_co, p1)) if len(no) == 3: myorder = [0, 2, 1] no = [no[i] for i in myorder] bm_el_type = "B31" if order == 1 else "B32" return Elem(segments[j], no, bm_el_type, parent=self._part.fem) elements = list(map(make_elem, range(len(segments)))) fem_sec_name = make_name_fem_ready(f"d{bm.name}_sec") if set_name in fem.elsets.keys(): fem_set = fem.elsets[set_name] for el in elements: el.fem_sec = fem_set.members[0].fem_sec fem_set.add_members(elements) else: fem_set = FemSet(set_name, elements, "elset", parent=fem) fem.sets.add(fem_set) fem.add_section( FemSection( fem_sec_name, "beam", fem_set, bm.material, bm.section, bm.ori[2], metadata=dict(beam=bm, numel=len(elements)), )) return elements
def add_bcs(self): funcs: List[Callable] = [self.c1, self.c2, self.c3, self.c4] fem_set_btn = self.fem.add_set(FemSet("fix", [], FemSet.TYPES.NSET)) nodes: List[Node] = [] for bc_loc in funcs: location = self.placement.origin + bc_loc(self._elevations[0]) nodes += self.fem.nodes.get_by_volume(location) fem_set_btn.add_members(nodes) self.fem.add_bc(Bc("bc_fix", fem_set_btn, [1, 2, 3]))
def convert_hinge(elem: Elem, hinge: Hinge): if hinge.constraint_ref is not None: return n = hinge.fem_node csys = hinge.csys d = hinge.retained_dofs n2 = Node(n.p, next(new_node_id), parent=elem.parent) elem.parent.nodes.add(n2, allow_coincident=True) i = elem.nodes.index(n) elem.nodes[i] = n2 if elem.eccentricity is not None: if elem.eccentricity.end1 is not None: if n == elem.eccentricity.end1.node: elem.eccentricity.end1.node = n2 if elem.eccentricity.end2 is not None: if n == elem.eccentricity.end2.node: elem.eccentricity.end2.node = n2 if n2.id not in constrain_ids: constrain_ids.append(n2.id) else: logging.error(f"Hinged node {n2} cannot be added twice to different couplings") return None m_set = FemSet(f"el{elem.id}_hinge{i + 1}_m", [n], "nset") s_set = FemSet(f"el{elem.id}_hinge{i + 1}_s", [n2], "nset") elem.parent.add_set(m_set) elem.parent.add_set(s_set) c = Constraint( f"el{elem.id}_hinge{i + 1}_co", Constraint.TYPES.COUPLING, m_set, s_set, d, csys=csys, ) elem.parent.add_constraint(c) hinge.constraint_ref = c logging.info(f"added constraint {c}")
def get_femsets(m, set_map, parent) -> FemSet: d = m.groupdict() isref = str_to_int(d["isref"]) fem_set = FemSet( d["set_name"].strip(), set_map[isref][0], set_map[isref][1], parent=parent, ) return fem_set
def beam_ex1(p1=(0, 0, 0), p2=(1.5, 0, 0), profile="IPE400", geom_repr=ElemType.SHELL) -> Assembly: mat_grade = CarbonSteel.TYPES.S355 bm = Beam("MyBeam", p1, p2, profile, Material("S355", mat_model=CarbonSteel(mat_grade))) bm.material.model.plasticity_model = DnvGl16Mat(bm.section.t_w, mat_grade) a = Assembly("Test", user=User("krande")) / [Part("MyPart") / bm] add_random_cutouts(bm) # Create a FEM analysis of the beam as a cantilever subjected to gravity loads p = a.get_part("MyPart") p.fem = bm.to_fem_obj(0.1, geom_repr) # Add a set containing ALL elements (necessary for Calculix loads). fs = p.fem.add_set(FemSet("Eall", [el for el in p.fem.elements], FemSet.TYPES.ELSET)) step = a.fem.add_step(StepImplicit("gravity", nl_geom=True, init_incr=100.0, total_time=100.0)) step.add_load(Load("grav", Load.TYPES.GRAVITY, -9.81 * 800, fem_set=fs)) fix_set = p.fem.add_set(FemSet("bc_nodes", get_beam_end_nodes(bm), FemSet.TYPES.NSET)) a.fem.add_bc(Bc("Fixed", fix_set, [1, 2, 3])) return a
def get_shell_elements(self, sh, order): """ :param sh: :param order: :return: """ import gmsh pl = self._pl_map[sh] assert isinstance(pl, Plate) try: get_elems = gmsh.model.mesh.getElements(2, sh) segments = get_elems[1][0] except BaseException as e: logging.debug(e) return [] fem_nodes = gmsh.model.mesh.getElements(2, sh)[2][0] elemTypes, elemTags, elemNodeTags = gmsh.model.mesh.getElements(2, sh) face, dim, morder, numv, parv, _ = gmsh.model.mesh.getElementProperties( elemTypes[0]) elem_type = gmsh_map[face] def make_elem(j): el_id = segments[j] nonlocal elem_type nonlocal numv nonlocal fem_nodes no = [] for i in range(numv): p1 = fem_nodes[numv * j + i] p1_co = gmsh.model.mesh.getNode(p1)[0] no.append(Node(p1_co, p1)) return Elem(el_id, no, elem_type, parent=self._part.fem) elements = list(map(make_elem, range(len(segments)))) femset = FemSet(f"el{pl.name}_set", elements, "elset") self._part.fem.add_set(femset) self._part.fem.add_section( FemSection( f"sh{pl.name}_sec", "shell", femset, pl.material, local_z=pl.n, thickness=pl.t, int_points=5, metadata=dict(beam=pl, numel=len(elements)), )) return elements
def test_negative_sec_contained(self): # A minor change in section box thickness sec = Section("myBG", from_str="BG800x400x20x40") mat = Material("my_mat") bm = Beam("my_beam", (0, 0, 0), (1, 0, 0), sec, mat) elem = Elem(1, [bm.n1, bm.n2], "B31") fem_set = FemSet("my_set", [elem], "elset") fem_sec = FemSection("my_sec", "beam", fem_set, mat, sec) p = get_fsec_bm_collection() self.assertFalse(fem_sec in p.fem.sections)
def build_mpc_for_end(elem, n_old, ecc, i): if n_old.id in edited_nodes.keys(): build_constraint(n_old, elem, ecc, i) else: mat = np.eye(3) new_p = np.dot(mat, ecc) + n_old.p n_new = Node(new_p, parent=elem.parent) elem.parent.nodes.add(n_new, allow_coincident=True) m_set = FemSet(f"el{elem.id}_mpc{i + 1}_m", [n_new], "nset") s_set = FemSet(f"el{elem.id}_mpc{i + 1}_s", [n_old], "nset") c = Constraint( f"el{elem.id}_mpc{i + 1}_co", Constraint.TYPES.MPC, m_set, s_set, mpc_type="Beam", parent=elem.parent, ) elem.parent.add_constraint(c) elem.nodes[i] = n_new edited_nodes[n_old.id] = n_new
def test_negative_sec_contained(self): # A minor change in section box thickness sec = Section('myBG', from_str='BG800x400x20x40') mat = Material('my_mat') bm = Beam('my_beam', (0, 0, 0), (1, 0, 0), sec, mat) elem = Elem(1, [bm.n1, bm.n2], 'B31') fem_set = FemSet('my_set', [elem], 'elset') fem_sec = FemSection('my_sec', 'beam', fem_set, mat, sec) p = get_fsec_bm_collection() self.assertFalse(fem_sec in p.fem.sections)
def test_negative_contained_shell_(part_with_shell): # Testing equal operator for change in element type mat = Material("my_mat") elem = Elem( 1, [Node((0, 0, 0)), Node((1, 0, 0)), Node((1, 1, 0)), Node((0, 1, 0))], "quad") fem_set = FemSet("my_set", [elem], "elset") fem_sec = FemSection("my_sec", "shell", fem_set, mat, thickness=0.01) assert fem_sec not in part_with_shell.fem.sections
def part_with_beam(): sec = Section("myIPE", from_str="BG800x400x30x40") mat = Material("my_mat") bm = Beam("my_beam", (0, 0, 0), (1, 0, 0), sec, mat) elem = Elem(1, [bm.n1, bm.n2], "line") p = Part("my_part") / bm fem_set = p.fem.sets.add(FemSet("my_set", [elem])) p.fem.sections.add( FemSection("my_sec", "line", fem_set, mat, sec, local_z=(0, 0, 1))) p.fem.elements.add(elem) return p
def test_negative_mat_contained(self): # A minor change in material property (S420 instead of S355) sec = Section('myBG', from_str='BG800x400x30x40') mat = Material('my_mat', CarbonSteel('S420')) bm = Beam('my_beam', (0, 0, 0), (1, 0, 0), sec, mat) elem = Elem(1, [bm.n1, bm.n2], 'B31') fem_set = FemSet('my_set', [elem], 'elset') fem_sec = FemSection('my_sec', 'beam', fem_set, mat, sec) p = get_fsec_bm_collection() self.assertFalse(fem_sec in p.fem.sections)
def find_bnmass(match) -> Mass: d = match.groupdict() nodeno = str_to_int(d["nodeno"]) mass_in = [ roundoff(d["m1"]), roundoff(d["m2"]), roundoff(d["m3"]), roundoff(d["m4"]), roundoff(d["m5"]), roundoff(d["m6"]), ] masses = [m for m in mass_in if m != 0.0] if checkEqual2(masses): mass_type = Mass.PTYPES.ISOTROPIC masses = [masses[0]] if len(masses) > 0 else [0.0] else: mass_type = Mass.PTYPES.ANISOTROPIC no = fem.nodes.from_id(nodeno) fem_set = fem.sets.add( FemSet(f"m{nodeno}", [no], FemSet.TYPES.NSET, parent=fem)) el_id = fem.elements.max_el_id + 1 elem = fem.elements.add( Elem(el_id, [no], Elem.EL_TYPES.MASS_SHAPES.MASS, None, parent=fem)) mass = Mass(f"m{nodeno}", fem_set, masses, Mass.TYPES.MASS, ptype=mass_type, parent=fem, mass_id=el_id) elset = fem.sets.add( FemSet(f"m{nodeno}", [elem], FemSet.TYPES.ELSET, parent=fem)) elem.mass_props = mass elem.elset = elset return mass
def test_negative_mat_contained(self): # A minor change in material property (S420 instead of S355) sec = Section("myBG", from_str="BG800x400x30x40") mat = Material("my_mat", CarbonSteel("S420")) bm = Beam("my_beam", (0, 0, 0), (1, 0, 0), sec, mat) elem = Elem(1, [bm.n1, bm.n2], "B31") fem_set = FemSet("my_set", [elem], "elset") fem_sec = FemSection("my_sec", "beam", fem_set, mat, sec) p = get_fsec_bm_collection() self.assertFalse(fem_sec in p.fem.sections)