diff --git a/CHANGES.md b/CHANGES.md index f422e37..bd8da14 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ ## Change Log +#### 1.1.4 +- Updates setup.py long_description to reStructuredText + #### 1.1.3 - Removes gmsh exerimental flag Mesh.CharacteristicLengthFromCurvature - Fixes typo in geometry.py diff --git a/dist/documentation.zip b/dist/documentation.zip index 3e4250d..8a7e2e0 100644 Binary files a/dist/documentation.zip and b/dist/documentation.zip differ diff --git a/dist/documentation/.buildinfo b/dist/documentation/.buildinfo new file mode 100644 index 0000000..3681249 --- /dev/null +++ b/dist/documentation/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: bcca5536fe390af833a0adc9dd39b00a +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/dist/documentation/_modules/index.html b/dist/documentation/_modules/index.html new file mode 100644 index 0000000..3e1ed79 --- /dev/null +++ b/dist/documentation/_modules/index.html @@ -0,0 +1,114 @@ + + + + + + + Overview: module code — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/base_classes.html b/dist/documentation/_modules/pycalculix/base_classes.html new file mode 100644 index 0000000..65e4718 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/base_classes.html @@ -0,0 +1,306 @@ + + + + + + + pycalculix.base_classes — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.base_classes

+"""This module stores base classes and functions.
+
+Attributes:
+    RESFILEDS (dict): stores results fields under results type
+
+        key (str):
+            - 'displ': displacement
+            - 'stress': stress
+            - 'strain': strain
+            - 'force': force
+        value (list):
+            - 'displ' = 'ux','uy','uz','utot'
+            - 'stress' = 'Sx','Sy','Sz','Sxy','Syz','Szx','Seqv','S1','S2','S3'
+            - 'strain' = 'ex','ey','ez','exy','eyz','ezx','eeqv','e1','e2','e3'
+            - 'force' = 'fx','fy','fz'
+    FIELDTYPE (dict): the inverse dict of RESFIELDS
+
+        For example: key 'ux' --> value: 'displ'
+"""
+from math import ceil
+from . import environment
+
+RESFIELDS = {}
+RESFIELDS['displ'] = 'ux,uy,uz,utot'.split(',')
+RESFIELDS['stress'] = 'Sx,Sy,Sz,Sxy,Syz,Szx,Seqv,S1,S2,S3'.split(',')
+RESFIELDS['strain'] = 'ex,ey,ez,exy,eyz,ezx,eeqv,e1,e2,e3'.split(',')
+RESFIELDS['force'] = 'fx,fy,fz'.split(',')
+
+#FIELDTYPE is a dict that inverts the RESFIELDS dictionary mapping.
+FIELDTYPE = {}
+for (k, v) in RESFIELDS.items():
+    for vi in v:
+        FIELDTYPE[vi] = k
+
+
[docs]class Idobj(object): + """Makes an object that stores an id number. + + This is a base class for nodes, lines, areas etc. + + Attributes: + id (int): the unique id number for the item + """ + + def __init__(self): + self.id = -1 + +
[docs] def set_id(self, id): + """Sets the id number """ + self.id = id
+ + def __hash__(self): + """Sets the hash of the item to the id number. + + This allows one to make sets of these items. + """ + return self.id
+ +
[docs]def plot_finish(plt, fname, display): + """Display and or save plot.""" + if fname != '': + # save the image + fname += '.png' + if environment.DPI != None: + plt.savefig(fname, dpi=environment.DPI, bbox_inches='tight') + else: + plt.savefig(fname, bbox_inches='tight') + print('File %s was saved.' % fname) + + if display: + plt.tight_layout() + plt.show() + + # remove all figures + plt.close()
+ +
[docs]def plot_set_bounds(plt, axials, radials): + """Sets the axial and radial bounds of the shown plot.""" + vert = max(radials) - min(radials) + horiz = max(axials) - min(axials) + vadder = (vert)/5 + hadder = (horiz)/5 + (vmax, vmin) = (max(radials)+vadder, min(radials)-vadder) + (hmax, hmin) = (max(axials)+hadder, min(axials)-hadder) + plt.xlim(hmin, hmax) + plt.ylim(vmin, vmax)
+ +
[docs]class Itemlist(list): + """Makes a custom list used to store lists of non-mesh items. + + non-mesh items = Point, Line, Arc, Area, Part, Components, Loads + This class allows us to automatically assign id numbers to items in the + list. Thes id numbers are needed when we send geometry out for meshing. + All instances of this class start as empty lists. + """ + + def __init__(self): + super().__init__() # these lists start empty + +
[docs] def get_ids(self): + """Returns a list of ids. Loops through all items in self.""" + return [a.id for a in self]
+ +
[docs] def get_next_id(self): + """Returns the next unused id.""" + ids = self.get_ids() + minid = 0 + if len(ids) == 0: + return minid # list is empty so return the minid number + else: + ids = sorted(ids) + maxid = ids[-1] + unused = list(set(list(range(minid, maxid+2))) - set(ids)) + return unused[0] # return first available id number
+ +
[docs] def append(self, item): + """Adds an item to the list and sets the item's id. + + Args: + item (Point or Line or Arc or Area or Part): item to add to the list + """ + idnum = self.get_next_id() + item.set_id(idnum) + super().append(item) + return item
+ + +
[docs]class Meshlist(list): + """Makes a custom list used to store lists of nodes and elements. + + All instances of this class start as empty lists. + """ + + def __init__(self): + list.__init__([]) + +
[docs] def get_minid(self): + """Returns the min id number in the list.""" + ids = [x.id for x in self] + return min(ids)
+ +
[docs] def get_maxid(self): + """Returns the max id number in the list.""" + ids = [x.id for x in self] + return max(ids)
+ +
[docs] def idget(self, idnum): + """Returns an item with the passed id number. + + Args: + idnum (int): passed id number, we want the item that has this + + Returns: + item or None: returns item if found, None if not found + """ + for item in self: + if item.id == idnum: + return item + #print ('ID %i was not found!' % (idnum)) + return None
+ +
[docs] def set_minid(self, val): + """Sets the min id to the passed val. + + Args: + val (int): New min id number in the list + """ + print('Re-indexing elements to min element number of: %i' % val) + print('Old min:%i max:%i' % (self.get_minid(), self.get_maxid())) + minid = self.get_minid() + offset = minid - val + for item in self: + item.id -= offset + print('New min:%i max:%i' % (self.get_minid(), self.get_maxid()))
+ + +
[docs]def chunk_list(inlist, size): + """Returns a list of lists where each list <= size length. + + Splits inlist into list of lists where each child list len <= size. + + Args: + inlist (list): list that we want cut into smaller lists + size (int): max length of small lists returned + + Returns: + res (list of lists): list of list where each child list <= size length + """ + res = [] + numlists = ceil(len(inlist)/size) + for ind in range(numlists): + res.append(inlist[ind*size:(ind+1)*size]) + return res
+ +
[docs]def listify(items): + """Returns a list of items. If items is an object it puts it in a list. + + If a list is passed, it returns the list without changing it. + If an object is passed, it returns a list with the object in it. + + Args: + items (object or list): item or list to convert to a list + """ + if not isinstance(items, list): + items = [items] + return items
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/cadimporter.html b/dist/documentation/_modules/pycalculix/cadimporter.html new file mode 100644 index 0000000..00bf300 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/cadimporter.html @@ -0,0 +1,436 @@ + + + + + + + pycalculix.cadimporter — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.cadimporter

+"""This module stores the CadImporter class, which is used to load CAD parts."""
+
+import collections
+import math
+import os
+# needed to prevent dxfgrabber from crashing on import
+os.environ['DXFGRABBER_CYTHON'] = 'OFF'
+import dxfgrabber # needed for dxf files
+import subprocess # needed to run gmsh to make geo files
+
+from . import geometry
+from . import partmodule
+
+
[docs]class CadImporter(object): + """Makes an object which can import cad parts. + + Args: + feamodel (FeaModel): model that we want to import parts into + layer (int): layer to import, all entities will be flattened + to one plane. -1 mean all layers, any other number is a specific layer + swapxy (bool): True rotates the part from x axial to y axial + scale (str): string telling the unit conversion scalar + + * Any option of 'fromunit-tounit' using the below units + * mm, m, in, ft + * Examples 'mm-m' 'm-in' 'ft-mm' 'in-ft' + * Default value is '' and does not apply a scale factor + + Attributes: + __fea (FeaModel): model + __fname (str): file we want to import + __layer (int): layer to import + __swapxy (bool): If true, swap part from xy to yx orientation + """ + + def __init__(self, feamodel, fname='', layer=-1, swapxy=False, scale=''): + self.__fea = feamodel + self.__fname = fname + self.__layer = layer + self.__swapxy = swapxy + self.__scale = scale + +
[docs] def load(self): + """Loads the self.__fname cad file + + Returns: + list: list of Part + """ + if self.__fname == '': + print('You must pass in a file name to load!') + return [] + + ext = os.path.splitext(self.__fname)[1] + first_pt = None + if len(self.__fea.points) > 0: + first_pt = self.__fea.points[0] + if ext == '.dxf': + parts = self.__load_dxf() + elif ext in ['.brep', '.brp', '.iges', '.igs', '.step', '.stp']: + self.__make_geo() + parts = self.__load_geo() + last_pt = None + if first_pt != None: + if len(self.__fea.points) > 2: + last_pt = self.__fea.points[-1] + if self.__scale != '': + # call scale + pass + return parts
+ + def __fix_tuple(self, xy_tup): + """Adjusts the point to be in the right plane (yx)""" + if self.__swapxy: + return xy_tup[::-1] + return xy_tup + + @staticmethod + def __find_make_pt(xy_tup, points_dict): + """ + Returns a point if it exists within geometry.ACC + or makes, stores and returns the point if it doesn't exist + """ + point = points_dict.get(xy_tup) + if point is not None: + return point + xy_point = geometry.Point(xy_tup[0], xy_tup[1]) + for realpoint in points_dict.values(): + dist = (xy_point - realpoint).length() + if dist < geometry.ACC: + return realpoint + points_dict[xy_tup] = xy_point + return xy_point + + def __get_pts_lines(self, lines, arcs): + """Returns a set of points, and a list of Lines and Arcs + + Args: + lines (dxfgrabber LINE list): dxf lines + arcs (dxfgrabber ARC list): dxf arcs + + Returns: + list: [list of points, list of Line and Arc] + """ + # store unique points + points_dict = {} + all_lines = [] + for ind, line in enumerate(lines): + tup = self.__fix_tuple((line.start[0], line.start[1])) + start = self.__find_make_pt(tup, points_dict) + tup = self.__fix_tuple((line.end[0], line.end[1])) + end = self.__find_make_pt(tup, points_dict) + line = geometry.Line(start, end) + all_lines.append(line) + for ind, arc in enumerate(arcs): + # dxfgrabber arcs are stored ccw when looking at xy plane + # x horizontal + # y vertical + tup = self.__fix_tuple((arc.center[0], arc.center[1])) + center = self.__find_make_pt(tup, points_dict) + sign = -1 + if self.__swapxy: + sign = 1 + startangle = arc.start_angle*sign + endangle = arc.end_angle*sign + angle = endangle - startangle + if arc.end_angle < arc.start_angle: + angle = angle + 360*sign + """ + print('---------------------------------------') + print('| ARC') + print('center: %s' % center) + print('startangle: %f' % startangle) + print('endangle: %f' % endangle) + print('traversed_angle: %f' % angle) + """ + start_vect = geometry.Point(0, arc.radius) + if self.__swapxy == False: + start_vect = geometry.Point(arc.radius, 0) + start_vect.rot_ccw_deg(arc.start_angle*sign) + end_vect = geometry.Point(0, arc.radius) + if self.__swapxy == False: + end_vect = geometry.Point(arc.radius, 0) + end_vect.rot_ccw_deg(arc.end_angle*sign) + start = center + start_vect + start_tup = (start.x, start.y) + end = center + end_vect + end_tup = (end.x, end.y) + start = self.__find_make_pt(start_tup, points_dict) + end = self.__find_make_pt(end_tup, points_dict) + rvect = start - center + if abs(angle) <= 90: + arc = geometry.Arc(start, end, center) + all_lines.append(arc) + print('1 arc made') + continue + #print(' %s' % arc) + pieces = math.ceil(abs(angle)/90) + print('%i arcs being made' % pieces) + points = [start, end] + # 2 pieces need 3 points, we have start + end already --> 1 pt + inserts = pieces + 1 - 2 + piece_ang = angle/pieces + #print('piece_ang = %f' % piece_ang) + while inserts > 0: + rvect.rot_ccw_deg(piece_ang) + point = center + rvect + tup = (point.x, point.y) + point = self.__find_make_pt(tup, points_dict) + points.insert(-1, point) + inserts = inserts - 1 + for ind in range(len(points)-1): + #print(' %s' % arc) + arc = geometry.Arc(points[ind], points[ind+1], center) + all_lines.append(arc) + for line in all_lines: + line.save_to_points() + return [list(points_dict.values()), all_lines] + + def __make_geo(self): + """Makes a gmsh geo file given a step, iges, or brep input""" + # gmsh freecad_part.iges -o out_iges.geo -0 + fname_list = self.__fname.split('.') + geo_file = fname_list[0]+'.geo' + runstr = "%s %s -o %s -0" % (environment.GMSH, self.__fname, geo_file) + print(runstr) + subprocess.call(runstr, shell=True) + print('Wrote file: %s' % geo_file) + + def __load_geo(self): + """Loads in a gmsh geo file and returns a list of parts + + Returns: + list: list of Part + """ + pass + # process any splines? and turn them into arcs + # http://www.mathopenref.com/constcirclecenter.html + # find max dist between points + # double it + # select two segments + # draw normal lines + # find intersections, that is the center + + @staticmethod + def __dangling_points(all_points): + return [point for point in all_points + if len(point.lines) == 1 and not point.arc_center] + + def __load_dxf(self): + """Loads in a dxf file and returns a list of parts + + Returns: + list: list of Part + """ + print('Loading file: %s' % self.__fname) + dwg = dxfgrabber.readfile(self.__fname) + lines = [item for item in dwg.entities if item.dxftype == 'LINE'] + arcs = [item for item in dwg.entities if item.dxftype == 'ARC'] + if self.__layer > -1: + lines = [item for item in lines if item.layer == self.__layer] + arcs = [item for item in arcs if item.layer == self.__layer] + print('File read.') + print('Loaded %i lines' % len(lines)) + print('Loaded %i arcs' % len(arcs)) + print('Loaded %i line segments, lines or arcs' % + (len(lines)+len(arcs))) + # get all points and Line and Arc using pycalculix entities + print('Converting to pycalculix lines arcs and points ...') + all_points, all_lines = self.__get_pts_lines(lines, arcs) + print('Loaded %i line segments, lines or arcs' % len(all_lines)) + print('Loaded %i points' % len(all_points)) + # for point in all_points: + # print('%s %s' % (point, point.lines)) + # for line in all_lines: + # print('%s %s' % (line, line.points)) + + # remove all lines that are not part of areas + dangling_points = self.__dangling_points(all_points) + pruned_geometry = bool(dangling_points) + while dangling_points: + for point in dangling_points: + all_points.remove(point) + print('Removed point= %s' % point) + dangling_line = list(point.lines)[0] + point.unset_line(dangling_line) + if dangling_line in all_lines: + all_lines.remove(dangling_line) + print('Removed line= %s' % dangling_line) + dangling_points = self.__dangling_points(all_points) + if pruned_geometry: + print('Remaining line segments: %i' % len(all_lines)) + print('Remaining points: %i' % len(all_points)) + + # make line all_loops now + all_loops = [] + line = all_lines[0] + this_loop = geometry.LineLoop() + while len(all_lines) > 0: + this_loop.append(line) + all_lines.remove(line) + if this_loop.closed == True: + all_loops.append(this_loop) + this_loop = geometry.LineLoop() + if all_lines: + line = all_lines[0] + continue + point = line.pt(1) + other_lines = point.lines - set([line]) + if len(other_lines) > 1: + # note: one could exclude connected segment nodes + # make disconnected line all_loops, then have another + # loop to connect thos disconnected line all_loops + print('One point was connected to > 2 lines.') + print('Only import simple part all_loops, or surfaces.') + raise Exception('Import geometry is too complex') + next_line = list(other_lines)[0] + if line.pt(1) != next_line.pt(0): + next_line.reverse() + line = next_line + + # find exterior loops + exterior_loops = [] + for ind, loop in enumerate(all_loops): + other_loops = all_loops[ind+1:] + other_loops.extend(exterior_loops) + is_exterior = True + for other_loop in other_loops: + if loop.inside(other_loop): + is_exterior = False + break + if is_exterior: + # exterior must be clockwise + if loop.ccw: + loop.reverse() + exterior_loops.append(loop) + # remove the found part exterior loops from all_loops + for exterior_loop in exterior_loops: + all_loops.remove(exterior_loop) + # each part in parts is a list of line all_loops + # [exterior, hole1, hole2] + parts = [[exterior_loop] for exterior_loop in exterior_loops] + # now place the child hole loops after the part exterior loop + for part_loops in parts: + exterior_loop = part_loops[0] + # find child holes + for hole_loop in all_loops: + if hole_loop.inside(exterior_loop): + hole_loop.hole = True + # holes must be ccw + if not hole_loop.ccw: + hole_loop.reverse() + part_loops.append(hole_loop) + # remove child holes from loop list + for hole_loop in part_loops[1:]: + all_loops.remove(hole_loop) + + # make parts + parts_list = [] + for part_loops in parts: + this_part = partmodule.Part(self.__fea) + for ind, loop in enumerate(part_loops): + is_hole = loop.hole + start = loop[0].pt(0) + this_part.goto(start.x, start.y, is_hole) + for item in loop: + if isinstance(item, geometry.Line): + end = item.pt(1) + this_part.draw_line_to(end.x, end.y) + elif isinstance(item, geometry.Arc): + end = item.pt(1) + center = item.actr + this_part.draw_arc(end.x, end.y, center.x, center.y) + parts_list.append(this_part) + print('Parts created: %i' % len(parts_list)) + return parts_list
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/components.html b/dist/documentation/_modules/pycalculix/components.html new file mode 100644 index 0000000..0d04a55 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/components.html @@ -0,0 +1,271 @@ + + + + + + + pycalculix.components — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.components

+"""This module defines the Component class, which is used to make components.
+
+Components are collections that loads are applied to.
+"""
+from . import base_classes
+from . import geometry
+
+
[docs]class Component(base_classes.Idobj): + """Makes a component to store loads, boundary conditions, matl, thickness. + + A component must store a list of items where the type is identical for + all items. + + Args: + item_list (list): list of items to store + ctype (str): type of component. Options: + + - 'nodes': all element nodes (corner and mid-side) + - 'n1': element corner nodes + - 'faces': faces + - 'elements': elements + + cname (str): component name + """ + + def __init__(self, item_list, ctype, cname=''): + self.items = item_list + self.ctype = ctype + self.name = cname+'_'+ctype + base_classes.Idobj.__init__(self) + + def __eq__(self, other): + """ Returns bool telling if self == other.""" + if isinstance(other, Component): + if self.items == other.items and self.ctype == other.ctype: + return True + return False + else: + return False + + def __hash__(self): + """Returns a hash of self instance.""" + return self.id + +
[docs] def set_name(self, name): + """Set the component name to the passed name. + + Args: + name (str): passed name, component name is changed to this + """ + self.name = name
+ +
[docs] def get_name(self): + """Returns the component name created with its id number.""" + return 'C'+str(self.id)
+ +
[docs] def get_children(self): + """Returns a list the child ctype items of the current component.""" + res = [] + for i in self.items: + children = getattr(i, self.ctype) + if isinstance(children, list): + res += children + else: + res += [children] + return res
+ +
[docs] def ccx(self): + """Writes a component for Calculix ccx solver. + + Returns a list strings, where each item is a line to write + to a Calculix .inp text file. + + Note: + Only node and element components should use this. + """ + res = [] + items_per_line = 6 + firstline = '' + if self.ctype == 'nodes': + # node component, displacement + firstline = '*NSET,NSET='+self.name + elif self.ctype == 'elements': + # element component, materials + firstline = '*ELSET,ELSET='+self.name + elif self.ctype == 'faces': + # face component, contact surface + firstline = '*SURFACE,NAME=%s,TYPE=ELEMENT' % self.name + res.append(firstline) + items = self.get_children() + if self.ctype == 'faces': + for face in items: + enum = face.element.id + fnum = face.id + res.append("%i,S%i" % (enum, fnum)) + elif self.ctype in ['nodes', 'elements']: + grouped_items = base_classes.chunk_list(items, items_per_line) + for group in grouped_items: + item_ids = [str(x.id) for x in group] + line = ', '.join(item_ids) + if group != grouped_items[-1]: + line += ',' + res.append(line) + return res
+ +
[docs] def write_cgx(self): + """Writes a component for Calculix cgx prerocessor. + + Returns a list strings, where each item is a line to write + to a Calculix .fbd text file. + + Note: + Only line and point node components should use this. + """ + res = [] + + # set the type of component + parent = 'l' + if (isinstance(self.items[0], geometry.Line) or + isinstance(self.items[0], geometry.Arc)): + parent = 'l' + elif isinstance(self.items[0], geometry.Point): + parent = 'p' + + # pull the list of entities + alist = ' '.join([a.get_name() for a in self.items]) + + if self.ctype == 'n1': + # write component of line element first order nodes + res.append('seta '+self.name+' '+parent+' '+alist) + elif self.ctype == 'nodes': + res.append('seta '+self.name+' '+parent+' '+alist) + res.append('comp '+self.name+' do') + # write component of all line element nodes + elif self.ctype == 'f': + # write component of line element faces + res.append('seta '+self.name+' '+parent+' '+alist) + res.append('comp '+self.name+' do') + res.append('comp '+self.name+' do') + return res
+ +
[docs] def write_gmsh(self): + """Writes a component for gmsh mesher. + + Returns a list strings, where each item is a line to write + to a Calculix .geo text file. + + Note: + Only line and point node components should use this. + """ + res = [] + + # set the type of component + parent = 'l' + if (isinstance(self.items[0], geometry.Line) or + isinstance(self.items[0], geometry.Arc)): + parent = 'l' + elif isinstance(self.items[0], geometry.Point): + parent = 'p' + + # pull the list of entities + alist = ','.join([str(a.id) for a in self.items]) + + line = '' + if parent == 'l': + line = "Physical Line('"+self.name+"') = {" + alist + '};' + elif parent == 'p': + line = "Physical Point('"+self.name+"') = {" + alist + '};' + res.append(line) + return res
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/connectors.html b/dist/documentation/_modules/pycalculix/connectors.html new file mode 100644 index 0000000..75b50e6 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/connectors.html @@ -0,0 +1,189 @@ + + + + + + + pycalculix.connectors — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.connectors

+"""This module stores connector classes, like Contact.
+"""
+from . import base_classes
+
+
[docs]class SurfaceInteraction(base_classes.Idobj): + """Makes a surface interaction object. + + Args: + int_type (str): interaction type + + * 'EXPONENTIAL' + * 'LINEAR' + + *args: following arguments + + * int_type = 'EXPONENTIAL' + * c0, then p0 must be passed + + * int_type = 'LINEAR' + * k must be passed + + """ + + def __init__(self, int_type, *args): + self.int_type = int_type + self.c0 = None + self.p0 = None + self.k = None + if self.int_type == 'EXPONENTIAL': + self.c0 = args[0] + self.p0 = args[1] + elif self.int_type == 'LINEAR': + self.k = args[0] + base_classes.Idobj.__init__(self) + + @property + def name(self): + """SurfaceInteraction name.""" + return 'SI%i' % self.id + +
[docs] def ccx(self): + """Writes the surface interaction for the ccx solver.""" + res = ["*SURFACE INTERACTION,NAME=%s" % self.name] + res.append("*SURFACE BEHAVIOR,PRESSURE-OVERCLOSURE=%s" % self.int_type) + if self.int_type == 'EXPONENTIAL': + res.append("%f,%f" % (self.c0, self.p0)) + elif self.int_type == 'LINEAR': + res.append("%e" % self.k) + #res.append("%e,1." % self.k) + return res
+ +
[docs]class Contact(base_classes.Idobj): + """Makes a contact which will be between lines which have faces on them. + + Args: + master_comp (Component): component of master element faces + slave_comp (Component): component of slave element faces + surf_int (SurfaceInteraction): object which stores closure behavior + surf_to_surf (bool): if True surface to surface is used, if False node + to surface is used + """ + + def __init__(self, master_comp, slave_comp, surf_int, surf_to_surf=True): + self.master_comp = master_comp + self.slave_comp = slave_comp + self.surf_int = surf_int + self.surf_to_surf = surf_to_surf + base_classes.Idobj.__init__(self) + + @property + def name(self): + """Contact name.""" + return 'CONT%i' % self.id + +
[docs] def ccx(self): + """Writes the contact pair for the ccx solver.""" + res = [] + end = '' + int_name = self.surf_int.name + m_name = self.master_comp.name + s_name = self.slave_comp.name + if self.surf_to_surf: + end = ',TYPE=SURFACE TO SURFACE' + line = "*CONTACT PAIR,INTERACTION=%s%s" % (int_name, end) + res.append(line) + line = "%s,%s" % (s_name, m_name) + res.append(line) + return res
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/environment.html b/dist/documentation/_modules/pycalculix/environment.html new file mode 100644 index 0000000..d4a77cc --- /dev/null +++ b/dist/documentation/_modules/pycalculix/environment.html @@ -0,0 +1,174 @@ + + + + + + + pycalculix.environment — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.environment

+"""This module sets the dpi and the paths to gmsh, ccx, and cgx.
+
+Attributes:
+    DPI (None or float): if high dpi windows 8 monitor value is set, otherwise
+        value is None.
+    CCX (str): path to Calculix ccx, the sovler.
+    CGX (str): path to Calculix cgx, the preprocessor/postprocessor/mesher.
+    GMSH (str): path to the Gmsh mesher.
+"""
+
+import sys # needed to check if 32 or 64 bit interpreter
+import os # used to delete files written by cgx
+import platform # need this to check for win and do the below dpi fix
+import ctypes # needed to see if it's 32-bit and to get correct win version
+
+# http://stackoverflow.com/questions/19128219/detect-windows-8-1-in-python/22325767#22325767
+
[docs]class OSVERSIONINFOEXW(ctypes.Structure): + """Returns object w/ attributes that will identify window os version""" + _fields_ = [('dwOSVersionInfoSize', ctypes.c_ulong), + ('dwMajorVersion', ctypes.c_ulong), + ('dwMinorVersion', ctypes.c_ulong), + ('dwBuildNumber', ctypes.c_ulong), + ('dwPlatformId', ctypes.c_ulong), + ('szCSDVersion', ctypes.c_wchar*128), + ('wServicePackMajor', ctypes.c_ushort), + ('wServicePackMinor', ctypes.c_ushort), + ('wSuiteMask', ctypes.c_ushort), + ('wProductType', ctypes.c_byte), + ('wReserved', ctypes.c_byte)]
+ +
[docs]def get_version(): + """Get's the OS version. Returns a float of OS_MAJOR.OS_MINOR + """ + os_version = OSVERSIONINFOEXW() + os_version.dwOSVersionInfoSize = ctypes.sizeof(os_version) + retcode = ctypes.windll.Ntdll.RtlGetVersion(ctypes.byref(os_version)) + if retcode != 0: + print("Failed to get OS version") + return -1.0 + + major = str(os_version.dwMajorVersion) + minor = str(os_version.dwMinorVersion) + return float(major+'.'+minor)
+ +
[docs]def get_dpi(): + """Returns an int of current DPI for windows or None.""" + if platform.system() == 'Windows': + version = get_version() + if version >= 6.2: + # Windows 8.0 is 6.2.9200 + # Only run on windows 8 or higher to check high dpi monitors + + user32 = ctypes.windll.user32 + w_curr = user32.GetSystemMetrics(0) + user32.SetProcessDPIAware() + w_phys = user32.GetSystemMetrics(0) + curr_dpi = round(w_phys*96/w_curr, 0) + + from pylab import rcParams + rcParams['figure.dpi'] = curr_dpi + return curr_dpi + else: + return None
+ +
[docs]def get_paths(): + """Returns a list of paths to: [ccx, cgx, gmsh].""" + ccx = 'ccx' + cgx = 'cgx' + gmsh = 'gmsh' + return [ccx, cgx, gmsh]
+ +DPI = get_dpi() +CCX, CGX, GMSH = get_paths() +
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/feamodel.html b/dist/documentation/_modules/pycalculix/feamodel.html new file mode 100644 index 0000000..64cf620 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/feamodel.html @@ -0,0 +1,2102 @@ + + + + + + + pycalculix.feamodel — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.feamodel

+""" This module stores the FeaModel class.
+
+This class is the highest level object in a pycalculix program.
+It stores all parts, loads, constraints, mesh, problem, and results_file
+objects.
+"""
+
+import matplotlib.pyplot as plt
+from matplotlib.collections import PatchCollection  # element plotting
+import matplotlib.colors as colors
+import matplotlib.cm as cmx
+import subprocess # used to launch meshers cgx and gmsh
+import os # used to delete files written by cgx
+from numpy.core.function_base import linspace # need to make contours
+
+from . import environment
+from . import base_classes
+from . import geometry
+from . import material
+from . import components
+from . import connectors
+from . import loads
+from . import mesh
+from . import partmodule
+from . import problem
+from . import selector
+
+# element colors, 0-1, 0=black, 1=whate
+ECOLOR = '.4'
+FCOLOR = '.9'
+CMAP = 'jet' # for results
+GEOM_CMAP = 'Pastel1' # color map for parts or areas
+
+
[docs]class FeaModel(object): + """Makes a FeaModel instance. + + Parts, area, lines, arcs, points, nodes, elements, faces, models, + components, and loads are stored in this object. + + Args: + model_name (str): project name for this FeaModel, this is a file prefix + ccx (None or str): path to Calculix ccx solver, pass this when you want + to overwite the default program location. + None means the default envionment.CCX is used. + cgx (None or str): path to Calculix cgx mesher, pass this when you want + to overwite the default program location. + None means the default envionment.CGX is used. + gmsh (None or str): path to gmsh mesher, pass this when you want + to overwite the default program location. + None means the default envionment.GMSH is used. + + Attributes: + fname (str): FeaModel project file name prefix + points (Itemlist): list of all Point + lines (Itemlist): list of all Line and Arc + signlines (Itemlist): list of all SignLine and SignArc + lineloops (Itemlist): list of all LineLoop, each contains SignLine SignArc + areas (Itemlist):list of all Area, each contains LineLoop(s) + parts (Itemlist): list of all Part + matls (Itemlist): list of all materials + components (Itemlist): list of all components + loads (dict): a dictionary of loads + + | Key (float): the load time point + | loads[time] = list of loads for that time step + | See method set_time to change the current time. + | Time = 0.0 stores constant loads, such as: + | material, thickness + + contacts (Itemlist): list of contacts + surfints (Itemlist): list of surface interactions + problems (Itemlist): list of problems + nodes (Meshlist): list of all mesh nodes + eshape (str): element shape + + - 'quad': quadrilateral elements (Default) + - 'tri': triangle elements + eorder (int): element order, 1 or 2 + + - 1: elements meshed with corner nodes only + - 2: (Default) elements meshed with corner and midside nodes + elements (Meshlist): list of all mesh elements + faces (list): list of all element faces, includes non-exterior ones + view (Selector): currently selected items + + Attributes: + parts: list of selected parts + areas: list of selected areas + lines: list of selected signed lines or arcs + points: list of selected points + elements: list of selected elements + faces: list of selected faces + nodes: list of selected nodes + time (float): current model time value, defaults to 1.0 + units (dict): a dict to store units for different fields + + Keys: + - 'displ': displacement or location + - 'force': force + - 'stress': stress + - 'temp': temperature + - 'density': density (mass/volume) + - 'time': time + + Returns: + Text string describing the units for the given key field. + + See the set_units method. + + For example when units have been set to metric, the below + values are returned. + + - 'dist' --> 'm' + - 'density' --> 'kg/(m^3)' + - 'time' --> 's' + """ + def __init__(self, model_name, ccx=None, cgx=None, gmsh=None): + self.fname = model_name + self.points = base_classes.Itemlist() + self.lines = base_classes.Itemlist() + self.signlines = base_classes.Itemlist() + self.lineloops = base_classes.Itemlist() + self.areas = base_classes.Itemlist() + self.parts = base_classes.Itemlist() + self.matls = base_classes.Itemlist() + self.components = base_classes.Itemlist() + self.loads = {} # store loads by time + self.contacts = base_classes.Itemlist() + self.surfints = base_classes.Itemlist() + self.problems = base_classes.Itemlist() + self.nodes = base_classes.Meshlist() + self.elements = base_classes.Meshlist() + self.faces = [] + self.view = selector.Selector(self) + self.time = 1.0 # 0 is model set-up, 1 is first step, etc + self.eshape = 'quad' + self.eorder = 2 + self.units = {} + + # fix the paths to the needed programs, ccx, cgx, and gmsh + if ccx != None: + environment.CCX = ccx + if cgx != None: + environment.CGX = cgx + if gmsh != None: + environment.GMSH = gmsh + +
[docs] def set_ediv(self, items, ediv): + """Sets the number of elements on the passed line. + + Args: + items (str or SignLine or SignArc or list): lines to set ediv on + + - str: 'L0' + - list of str ['L0', 'L1'] + - list of SignLine or SignArc part.bottom or part.hole[-1] + + ediv (int): number of elements to mesh on the line + """ + items = self.get_items(items) + for line in items: + line.set_ediv(ediv)
+ +
[docs] def set_esize(self, items, esize): + """Sets the element size on the passed line. + + Args: + items (str or SignLine or SignArc or list): lines or points to set esize on + + - str: 'L0' + - list of str ['L0', 'L1', 'P3'] + - list of SignLine or SignArc part.bottom or part.hole[-1] + + esize (float): size of the mesh elements on the line + """ + items = self.get_items(items) + for item in items: + item.set_esize(esize)
+ +
[docs] def set_units(self, dist_unit='m', cfswitch=False): + """Sets the units that will be displayed when plotting. + + Picks a unit set based on the passed distance unit. + That unit set is printed to the console when set. + Defaults to MKS units (meter-kilogram-second) + + ======== ===== ====== =========== =============== ==== + Distance Force Stress Temperature Density Time + ======== ===== ====== =========== =============== ==== + 'm' 'N' 'Pa' 'K' 'kg/(m^3)' 's' + 'mm' 'N' 'MPa' 'K' 'tonne/(mm^3)' 's' + 'in' 'lbf' 'psi' 'R' 'slinch/(in^3)' 's' + 'ft' 'lbf' 'psf' 'R' 'slug/(ft^3)' 's' + ======== ===== ====== =========== =============== ==== + + See get_units method or returning text strings based on unit types. + + Args: + dist_unit (str): string of distance unit. Options: + + - 'm': meter + - 'mm': milimeter + - 'in': inch + - 'ft': foot + + cfswitch (bool): Celsius/Fahrenheit temperature switch. + Default is False. + + If True, this switches temperature from K-->C or from R-->F + Default keeps units as shown in above table. + """ + keys = ['dist', 'force', 'stress', 'temp', 'density', 'time'] + + m_newton = ['m', 'N', 'Pa', 'K', 'kg/(m^3)', 's'] + mm_newton = ['mm', 'N', 'MPa', 'K', 'tonne/(mm^3)', 's'] + in_lbf = ['in', 'lbf', 'psi', 'R', 'slinch/(in^3)', 's'] + ft_lbf = ['ft', 'lbf', 'psf', 'R', 'slug/(ft^3)', 's'] + unit_systems = [m_newton, mm_newton, in_lbf, ft_lbf] + vals = [usys for usys in unit_systems if usys[0] == dist_unit][0] + + if cfswitch: + # switches K-->C and R-->F + newunit = {'K':'C', 'R':'F'} + vals['temp'] = newunit[vals['temp']] + + # set values + adict = dict(zip(keys, vals)) + adict['displ'] = adict['dist'] + print('================================================') + print('Units have been set to %s_%s' % (adict['dist'], adict['force'])) + for key in adict: + print('For %s use %s' % (key, adict[key])) + print('================================================') + self.units = adict
+ +
[docs] def get_units(self, *args): + """Returns units for the passed arguments. Accepts and returns a list + of strings. + + Options for inputs: + - 'dist' + - 'displ' (same units as 'dist') + - 'force' + - 'stress' + - 'temp' + - 'density' + - 'time' + """ + res = [] + for arg in args: + mystr = '' + if arg in self.units: + # check if the requested item is in the units dict + mystr = ' ('+ self.units[arg] + ')' + elif arg in base_classes.FIELDTYPE: + # return units on results dict item, ex: stress, strain etc + ftype = base_classes.FIELDTYPE[arg] + if ftype in self.units: + mystr = ' ('+ self.units[ftype] + ')' + res.append(mystr) + return res
+ +
[docs] def set_time(self, time): + """Sets the time in the FeaModel (preprocessor). + + This time is used when setting loads. + + Args: + time (float): the time to set + """ + self.time = time
+ +
[docs] def get_item(self, item): + """Returns an item given a string identifying the item. + + Args: + item (str): 'A0' or 'P0' or 'L0' etc. + """ + if item[0] == 'P': + # get point + items = self.points + num = int(item[1:]) + res = [a for a in items if a.id == num] + return res[0] + elif item[0] == 'L' or item[1] == 'L': + # get line + items = self.signlines + num = int(item[1:]) + items = [a for a in items if a.get_name() == item] + return items[0] + elif item[0] == 'A': + # get area + items = self.areas + num = int(item[1:]) + items = [a for a in items if a.id == num] + return items[0] + elif item[0] == 'E': + # get element + items = self.elements + items = [a for a in items if a.get_name() == item] + return items[0] + elif item[0] == 'N': + # get node + items = self.nodes + items = [a for a in items if a.get_name() == item] + return items[0] + else: + print('Unknown item! Please pass the name of a point, line or area!')
+ +
[docs] def get_items(self, items): + """Returns a list of correctly typed items. + + Input can be a single string or item, or a list of strings identifying + items. + """ + res_items = base_classes.listify(items) + for ind, item in enumerate(res_items): + if isinstance(item, str): + res_items[ind] = self.get_item(item) + return res_items
+ +
[docs] def make_matl(self, name): + """Makes and returns a new material. + + Args: + name (str): the material's name + """ + mat = material.Material(name) + self.matls.append(mat) + return mat
+ +
[docs] def make_part(self): + """Makes and returns a new part.""" + #p = part.Part(self) + #self.parts.append(p) + return partmodule.Part(self)
+ +
[docs] def make_problem(self, problem_type='struct', parts='all'): + """Makes and returns a new problem, which can be solved. + + Args: + problem_type (str): problem type, options: + 'struct': structural + parts (str Part or list of Part): Parts the model will analyze. + + Options: + - 'all': add all parts. This is the default. + - Part: add the single part + - list of Part: add these parts + """ + if parts == 'all': + parts = self.parts + prob = problem.Problem(self, problem_type, parts) + return prob
+ +
[docs] def print_summary(self): + """Prints a summary of the items in the database. + """ + items = ['parts', 'areas', 'signlines', 'points', 'elements', 'faces', + 'nodes'] + names = ['parts', 'areas', 'lines', 'points', 'elements', 'faces', + 'nodes'] + + spacer = '----------------------------------' + print(spacer) + print("Items in the database:") + for (item_str, name) in zip(items, names): + items = getattr(self, item_str) + print(' %s: %i' % (name, len(items))) + print(spacer)
+ +
[docs] def plot_nodes(self, fname='', display=True, title='Nodes', nnum=False): + """Plots the selected nodes. + + Args: + fname (str): png image file prefix, if given, image will be saved + display (bool): if True, interactive plot will be shown, if False + plot will not be shown + title (str): the plot's title + nnum (bool): if True node numbers are plotted + """ + nodes = self.view.nodes + if len(nodes) > 0: + # plotting elements + fig = plt.figure() + axis = fig.add_subplot(111) + + # plot nodes, this is quicker than individual plotting + axs = [node.y for node in nodes] + rads = [node.x for node in nodes] + axis.scatter(axs, rads, s=7, color='black') + if nnum: + for node in nodes: + node.label(axis) + + # set units + [d_unit] = self.get_units('dist') + plt.title(title) + plt.xlabel('axial, y'+d_unit) + plt.ylabel('radial, x'+d_unit) + plt.axis('scaled') + + # extract max and min for plot window + radials = [n.x for n in nodes] + axials = [n.y for n in nodes] + + # finish pot + base_classes.plot_set_bounds(plt, axials, radials) + base_classes.plot_finish(plt, fname, display) + + else: + # no elements exist or no elemnts are selected + res = '' + if len(self.nodes) == 0: + res = 'No nodes exist! Try meshing your parts!' + else: + res = 'No nodes are selected! Select some!' + print(res)
+ +
[docs] def plot_elements(self, fname='', display=True, title='Elements', + enum=False, nshow=False, nnum=False): + """Plots the selected elements. + + Args: + fname (str): png image file prefix, if given, image will be saved + display (bool): if True, interactive plot will be shown, if False + plot will not be shown + title (str): the plot's title + enum (bool): if True element numbers are plotted + nshow (bool): True=plot nodes, False=don't plot them + nnum (bool): if True node numbers are plotted + """ + nodes = self.view.nodes + elements = self.view.elements + if len(elements) > 0: + # plotting elements + fig = plt.figure() + axis = fig.add_subplot(111) + polys = [] + for element in elements: + poly = element.get_poly() + polys.append(poly) + coll = PatchCollection(polys, facecolors=FCOLOR, edgecolors=ECOLOR) + axis.add_collection(coll) + + # plot element numbers + if enum: + for element in elements: + element.label(axis) + + # plot nodes, this is quicker than individual plotting + if nshow: + axs = [node.y for node in nodes] + rads = [node.x for node in nodes] + axis.scatter(axs, rads, s=7, color='black') + if nnum: + for node in nodes: + node.label(axis) + + # set units + [d_unit] = self.get_units('dist') + plt.title(title) + plt.xlabel('axial, y'+d_unit) + plt.ylabel('radial, x'+d_unit) + plt.axis('scaled') + + # extract nodes for elements if no nodes are selected + if len(nodes) == 0: + tmp = set() + for element in elements: + tmp.update(element.nodes) + nodes = list(tmp) + # extract max and min for plot window + radials = [n.x for n in nodes] + axials = [n.y for n in nodes] + + # finish pot + base_classes.plot_set_bounds(plt, axials, radials) + base_classes.plot_finish(plt, fname, display) + + else: + # no elements exist or no elemnts are selected + res = '' + if len(self.elements) == 0: + res = 'No elements exist! Try meshing your parts!' + else: + res = 'No elements are selected! Select some!' + print(res)
+ +
[docs] def plot_pressures(self, fname='', display=True): + """Plots the load step pressures on the passed part(s) or selected + elements. + + This is an element plot, with arrows showing pressure magnitude and + directions. + + Args: + fname (str): png image file prefix, if given, image will be saved + display (bool): if True, interactive plot will be shown, if False + plot will not be shown. Default = True + """ + elements = self.view.elements + faces = self.view.faces + nodes = self.view.nodes + + # plot all elements and store length, length determins min pressure arrow + if len(elements) > 0: + # plotting elements + fig = plt.figure() + axis = fig.add_subplot(111, aspect='equal') + + # plot polys and calculate avg face length + polys = [] + face_len = [] + for element in elements: + face_len.append(element.face[1].length()) + poly = element.get_poly() + polys.append(poly) + coll = PatchCollection(polys, edgecolors=ECOLOR, facecolors=FCOLOR) + axis.add_collection(coll) + face_len = sum(face_len)/len(face_len) + + # store pressures we'll want to plot: list of [face, pval] + plist = [] + for load in self.loads[self.time]: + if load.ltype in ['press', 'press_fluid']: + loadlist = load.get_list() + for [face, pval] in loadlist: + if face in faces: + plist.append([face, pval]) + pressures = [abs(pval) for [face, pval] in plist] + + # set max and min pressure bounds + pmin = min(pressures) + pmax = max(pressures) + # extract nodes for elements if no nodes are selected + if len(nodes) == 0: + tmp = set() + for element in elements: + tmp.update(element.nodes) + nodes = list(tmp) + radials = [p.x for p in nodes] + axials = [p.y for p in nodes] + # arrow length = arrow_min + (pval-pmin)*mult + arrow_min = face_len + mult = 0 + if pmax != pmin: + adelta = max(axials) - min(axials) + rdelta = max(radials) - min(radials) + delta = max(adelta, rdelta) + dist = delta*0.2 + mult = dist/(pmax - pmin) + + # make tick list for later plot, and color map + cbar_val = None + tick_list = [] + cmap = None + if pmax != pmin: + # we have a range of values we're plotting + tick_list = linspace(pmin, pmax, 8) + cmap = plt.get_cmap(CMAP) + else: + cbar_val = pmin + pmax = pmin + 1.0 + pmin = pmin - 1.0 + tick_list = [pmin, cbar_val, pmax] # default 3 values to plot one solid color + cmap = colors.ListedColormap(['b', 'b']) # default to plot one val + + # set color contours for arrows + cnorm = colors.Normalize(vmin=pmin, vmax=pmax) + scalarmap = cmx.ScalarMappable(norm=cnorm, cmap=cmap) + scalarmap.set_array([]) + + # make arrows, store axials + radials + for [face, pval] in plist: + [face_point, unit] = face.get_mnorm() + arrow_length = arrow_min + (abs(pval) - pmin)*mult + other_point = face_point + unit*arrow_length + radials.append(other_point.x) + axials.append(other_point.y) + + # compression + tail = other_point + delta = face_point - other_point + if pval < 0: + # tension + tail = face_point + delta = other_point - face_point + + headwidth = face_len*0.2 + headlength = face_len*0.3 + colorval = scalarmap.to_rgba(abs(pval)) + plt.arrow(tail.y, tail.x, delta.y, delta.x, + color=colorval, + head_width=headwidth, head_length=headlength, + length_includes_head=True) + + # set the horizontal and vertical axes + base_classes.plot_set_bounds(plt, axials, radials) + + # set units and titles + [d_unit, p_unit, t_unit] = self.get_units('dist', 'stress', 'time') + tstr = 'Pressures %s\nTime=%f%s' % (p_unit, self.time, t_unit) + plt.title(tstr) + plt.xlabel('axial, y'+d_unit) + plt.ylabel('radial, x'+d_unit) + + # set the colorbar + cbar = plt.colorbar(scalarmap, orientation='vertical', + ticks=tick_list) + if cbar_val != None: + cbar.ax.set_yticklabels(['', str(cbar_val), '']) + base_classes.plot_finish(plt, fname, display) + + else: + res = '' + if len(self.elements) == 0: + res = 'No elements exist! Try meshing your parts!' + else: + res = 'No elements are selected! Select some!' + print(res)
+ +
[docs] def plot_constraints(self, fname='', display=True): + """Plots the constraints on the passed part(s) or selected items. + + This is an element and node plot, with arrows showing displacement + constraints. + + Args: + fname (str): png image file prefix, if given, image will be saved + display (bool): if True, interactive plot will be shown, if False + plot will not be shown. Default = True + """ + elements = self.view.elements + nodes = self.view.nodes + + # plot all elements + if len(elements) > 0: + # plotting elements + fig = plt.figure() + axis = fig.add_subplot(111, aspect='equal') + + # plot polys and calculate avg face length + polys = [] + face_len = [] + for element in elements: + face_len.append(element.face[1].length()) + poly = element.get_poly() + polys.append(poly) + coll = PatchCollection(polys, edgecolors=ECOLOR, facecolors=FCOLOR) + axis.add_collection(coll) + face_len = sum(face_len)/len(face_len) + + # store displacements we'll plot: list of [node, dict ux,uy,uz] + ulist = [] + vals = [] + for load in self.loads[self.time]: + if load.ltype in ['ux', 'uy', 'uz']: + alist = load.get_list() + for nodelist in alist: + node = nodelist[0] + if node in nodes: + ulist += [nodelist] + vals += list(nodelist[1].values()) + + # check min and max bounds + pmin = min(vals) + pmax = max(vals) + + # make tick list for later plot, and color map + cbar_val = None + tick_list = [] + cmap = None + if pmax != pmin: + # we have a range of values we're plotting + tick_list = linspace(pmin, pmax, 8) + cmap = plt.get_cmap(CMAP) + else: + cbar_val = pmin + pmax = pmin + 1.0 + pmin = pmin - 1.0 + tick_list = [pmin, cbar_val, pmax] # default 3 values to plot one solid color + cmap = colors.ListedColormap(['b', 'b']) # default to plot one val + + # set color contours for arrows + cnorm = colors.Normalize(vmin=pmin, vmax=pmax) + scalarmap = cmx.ScalarMappable(norm=cnorm, cmap=cmap) + scalarmap.set_array([]) + + # make arrows for displacements + # extract nodes for elements if no nodes are selected + if len(nodes) == 0: + tmp = set() + for element in elements: + tmp.update(element.nodes) + nodes = list(tmp) + radials = [p.x for p in nodes] + axials = [p.y for p in nodes] + pvect = {'ux':geometry.Point(1, 0, 0), + 'uy':geometry.Point(0, 1, 0), + 'uz':geometry.Point(0, 0, 1)} + for [node, udict] in ulist: + for (key, val) in udict.items(): + headw = face_len*0.4 + headl = headw + point = geometry.Point(node.x, node.y, node.z) + unit = pvect[key] + + # nonzero displacement, draw from node + tail = point + head = tail + unit*val + store_point = head + if val == 0: + # zero displ, draw to node + head = point + tail = head - unit*face_len + store_point = tail + delta = head - tail + radials.append(store_point.x) + axials.append(store_point.y) + + colorVal = scalarmap.to_rgba(val) + plt.arrow(tail.y, tail.x, delta.y, delta.x, + color=colorVal, head_width=headw, + head_length=headl, length_includes_head=True) + + # set the horizontal and vertical axes + base_classes.plot_set_bounds(plt, axials, radials) + + # set units + titles + [d_unit, t_unit] = self.get_units('dist', 'time') + tstr = 'Constraints %s\nTime=%f%s' % (d_unit, self.time, t_unit) + plt.title(tstr) + plt.xlabel('axial, y'+d_unit) + plt.ylabel('radial, x'+d_unit) + + # set the colorbar + cbar = plt.colorbar(scalarmap, orientation='vertical', + ticks=tick_list) + if cbar_val != None: + cbar.ax.set_yticklabels(['', str(cbar_val), '']) + base_classes.plot_finish(plt, fname, display) + + else: + res = '' + if len(self.elements) == 0: + res = 'No elements exist! Try meshing your parts!' + else: + res = 'No elements are selected! Select some!' + print(res)
+ +
[docs] def plot_multiple(self, fname='', display=True, title='', + styledict={'labels':['points', 'lines', 'areas', 'parts'], + 'items':['points, lines, areas']}): + """Plots items of type styledict['items'], labels styledict['labels'] + + Only the items currently selected will be plotted. + """ + # http://stackoverflow.com/questions/470690/how-to-automatically-generate-n-distinct-colors/4382138#4382138 + # http://stackoverflow.com/questions/2328339/how-to-generate-n-different-colors-for-any-natural-number-n + # start plotting + fig = plt.figure() + axis = fig.add_subplot(111) + + # check if we're plotting parts or areas and pick a color map + colormaker, ids = None, None + color_plot = False + if 'parts' in styledict['items']: + color_plot = True + ids = [item.id for item in self.view.parts] + if 'areas' in styledict['items']: + styledict['items'].remove('areas') + if 'areas' in styledict['items']: + color_plot = True + ids = [item.id for item in self.view.areas] + if color_plot: + cmap = plt.get_cmap(GEOM_CMAP) + norm = colors.Normalize(vmin=min(ids), vmax=max(ids)) + colormaker = cmx.ScalarMappable(norm=norm, cmap=cmap) + + # plot the items + for item_type in ['points', 'lines', 'areas', 'parts']: + plot_on = item_type in styledict['items'] + label_on = item_type in styledict['labels'] + items = getattr(self.view, item_type) + if plot_on and label_on: + for item in items: + if color_plot and item_type in ['areas', 'parts']: + color = colormaker.to_rgba(item.id) + item.plot(axis, True, color) + else: + item.plot(axis, True) + elif plot_on: + for item in items: + if color_plot and item_type in ['areas', 'parts']: + color = colormaker.to_rgba(item.id) + item.plot(axis, False, color) + else: + item.plot(axis, False) + elif label_on: + for item in items: + item.label(axis) + + # set the horizontal and vertical axes + points = self.view.points + radials = [point.x for point in points] + axials = [point.y for point in points] + base_classes.plot_set_bounds(plt, axials, radials) + + # set units + [d_unit] = self.get_units('dist') + + # show plot + plt.title(title) + plt.xlabel('axial, y'+d_unit) + plt.ylabel('radial, x'+d_unit) + axis.set_aspect('equal') + base_classes.plot_finish(plt, fname, display)
+ +
[docs] def plot_parts(self, fname='', display=True, title='Parts', label=True): + """Plots selected parts + + Defaults to displaying labels on: parts and filling all part areas. + + Args: + fname (str): png image file prefix, if given, image will be saved + display (bool): if True, interactive plot will be shown, if False + plot will not be shown. Default = True + title (str): the plot's title + label (bool): if True all Part objects will be labeled + """ + item = 'parts' + styledict = {'items':[], 'labels':[]} + if label: + styledict['items'].append(item) + styledict['labels'].append(item) + else: + styledict['items'].append(item) + + self.plot_multiple(fname, display, title, styledict)
+ +
[docs] def plot_areas(self, fname='', display=True, title='Areas', label=True): + """Plots selected areas + + Defaults to displaying labels on: areas and filling all part areas. + + Args: + fname (str): png image file prefix, if given, image will be saved + display (bool): if True, interactive plot will be shown, if False + plot will not be shown. Default = True + title (str): the plot's title + label (bool): if True all areas will be labeled + """ + item = 'areas' + styledict = {'items':[], 'labels':[]} + if label: + styledict['items'].append(item) + styledict['labels'].append(item) + else: + styledict['items'].append(item) + + self.plot_multiple(fname, display, title, styledict)
+ +
[docs] def plot_lines(self, fname='', display=True, title='Lines', label=True): + """Plots selected lines and arcs + + Defaults to displaying labels on: lines and arcs + + Args: + fname (str): png image file prefix, if given, image will be saved + display (bool): if True, interactive plot will be shown, if False + plot will not be shown. Default = True + title (str): the plot's title + label (bool): if True all lines and arcs will be labeled + """ + item = 'lines' + styledict = {'items':[], 'labels':[]} + if label: + styledict['items'].append(item) + styledict['labels'].append(item) + else: + styledict['items'].append(item) + + self.plot_multiple(fname, display, title, styledict)
+ +
[docs] def plot_points(self, fname='', display=True, title='Points', label=True): + """Plots selected points + + Defaults to displaying labels on: points + + Args: + fname (str): png image file prefix, if given, image will be saved + display (bool): if True, interactive plot will be shown, if False + plot will not be shown. Default = True + title (str): the plot's title + label (bool): if True all points will be labeled + """ + item = 'points' + styledict = {'items':[], 'labels':[]} + if label: + styledict['items'].append(item) + styledict['labels'].append(item) + else: + styledict['items'].append(item) + + self.plot_multiple(fname, display, title, styledict)
+ +
[docs] def plot_geometry(self, fname='', display=True, title='Geometry', pnum=True, + lnum=True, anum=True, afill=True): + """Plots selected geometry items: areas, lines, points. + + Defaults to displaying labels on: areas, lines, and points, and filling + in the areas. + + Args: + fname (str): png image file prefix, if given, image will be saved + display (bool): if True, interactive plot will be shown, if False + plot will not be shown. Default = True + title (str): the plot's title + pnum (bool): if True all Point objects will be labeled + lnum (bool): if True all Line and Arc objects will be labeled + anum (bool): if True all Area objects will be labeled + afill (bool): if True all Area objects will be filled + """ + styledict = {'items':[], 'labels':[]} + if pnum: + styledict['items'].append('points') + styledict['labels'].append('points') + if lnum: + styledict['items'].append('lines') + styledict['labels'].append('lines') + if anum: + styledict['labels'].append('areas') + if afill: + styledict['items'].append('areas') + elif anum == False and afill: + styledict['items'].append('areas') + + self.plot_multiple(fname, display, title, styledict)
+ + @staticmethod + def __get_cname(items): + """Returns a component name prefix, for labeling lists of items. + + Args: + items (list): a list of the items we'll be making a component of + """ + cname = '' + if len(items) == 1: + if items[0] == 'all': + cname = 'all' + else: + cname = items[0].get_name() + else: + cname = items[0].get_name()+'-'+items[-1].get_name() + return cname + + def __get_make_comp(self, comp): + """Stores component if it doesn't exist, returns it if it does. + + Args: + comp (Component): component we want to store/get + """ + if comp not in self.components: + comp = self.components.append(comp) + else: + ind = self.components.index(comp) + comp = self.components[ind] + return comp + + def __get_make_surfint(self, surfint): + """Stores surfac interaction if it doesn't exist, returns it if it does. + + Args: + surfint (connectors.SurfaceInteraction): item to get or make + """ + items = [item for item in self.surfints if item.int_type == surfint.int_type] + if surfint.int_type == 'LINEAR': + for item in items: + if item.k == surfint.k: + return item + elif surfint.type == 'EXPONENTIAL': + for item in items: + if item.c0 == surfint.c0 and item.p0 == surfint.p0: + return item + surfint = self.surfints.append(surfint) + return surfint + +
[docs] def register(self, item): + """Adds an item to the feamodel. + + Item is added to the correct list and its id is updated. + Allowed Items: + + * Point + * Line + * Arc + * SignLine + * SignArc + * LineLoop + * Area + * Part + """ + class_name = item.__class__.__name__ + class_to_list = {'Point': 'points', + 'Line': 'lines', + 'Arc': 'lines', + 'SignLine': 'signlines', + 'SignArc': 'signlines', + 'LineLoop': 'lineloops', + 'Area': 'areas', + 'Part': 'parts'} + if class_name in class_to_list: + list_name = class_to_list[class_name] + getattr(self, list_name).append(item) + else: + print('ERROR: the item you passed must be a geometry item!') + print('You passed a %s' % class_name) + message = ("Allowed items: Point, Line, Arc, SignLine, SignArc, " + "LineLoop, Area, Part") + print(message)
+ + def __add_load(self, load, time): + """Adds a load to the FeaModel. + + Args: + load (Load or Load_Linear): th eload to add + time (float): the load's time point. The first time point is 1.0 + """ + if time in self.loads: + self.loads[time].append(load) + else: + self.loads[time] = [load] + return load + +
[docs] def scale(self, unitstr, point_first=None, point_last=None): + """Scales the points from [point_first, ..., point_last] using unitstr + + If point_first and point_last are both None, all points are scaled. + + Args: + unitstr (str): string scalar 'fromunit-tounit' using the below units: + + * mm, m, in, ft + * Examples 'mm-m' 'm-in' 'ft-mm' 'in-ft' + * Default value is '' and does not apply a scale factor + + point_first (Point or None or str): the first point to scale, + string point names may be passed + point_last (Point or None or str): the last point to scale, + string point names may be passed + """ + # convert to points if passing in string point names 'P0' + if isinstance(point_first, str): + point_first = self.get_item(point_first) + if isinstance(point_last, str): + point_last = self.get_item(point_last) + + ind_first = 0 + if point_first != None: + ind_first = self.points.index(point_first) + ind_last = -1 + if point_last != None: + ind_last = self.points.index(point_last) + + unit_size = {'m':1.0, 'mm':.001, 'ft':.3048, 'in':0.0254} + units = unitstr.split('-') + from_unit, to_unit = units[0], units[1] + scalar = unit_size[from_unit]/unit_size[to_unit]
+ # scale all points in set + # update all dependent arcs + # update all dependent line loops + # update all dependent areas + # update all dependent parts + +
[docs] def set_gravity(self, grav, items): + """Sets gravity on the elements in items. + + Assumes gravity acts in the -x direction with magnitude grav. + + Args: + grav (float): gravity acceleration, MUST BE POSTIVE + items (Area or Part or list): items gravity acts on + + - Area: gravity acts on elements in this area + - Part: gravity acts on elements in this part + - list of Part or Area: gravity acts on their elements + """ + items = self.get_items(items) + ctype = 'elements' + cname = self.__get_cname(items) + + # make compoenet + comp = components.Component(items, ctype, cname) + comp = self.__get_make_comp(comp) + + # make load + ltype = 'gravity' + time = self.time + load = loads.ConstLoad(ltype, comp, grav) + self.__add_load(load, time) + return load
+ +
[docs] def set_rpm(self, rpm, items): + """Sets rpm rotation load on the items. + + Args: + rpm (float): rotation per minute + items (Area or Part or list): items rotation acts on + + - Area: rotation acts on elements in this area + - Part: rotation acts on elements in this part + - list of Part or Area: rotation acts on their elements + """ + # applies rpm to the items + items = self.get_items(items) + ctype = 'elements' + cname = self.__get_cname(items) + + # make compoenet + comp = components.Component(items, ctype, cname) + comp = self.__get_make_comp(comp) + + # make load + ltype = 'rpm' + time = self.time + load = loads.ConstLoad(ltype, comp, rpm) + self.__add_load(load, time) + return load
+ +
[docs] def set_radps(self, radps, items): + """Sets radians per second rotation load on the items. + + Args: + radps (float): radians per second + items (Area or Part or list): items rotation acts on + + - Area: rotation acts on elements in this area + - Part: rotation acts on elements in this part + - list of Part or Area: rotation acts on their elements + """ + items = self.get_items(items) + ctype = 'elements' + cname = self.__get_cname(items) + + # make compoenet + comp = components.Component(items, ctype, cname) + comp = self.__get_make_comp(comp) + + # make load + ltype = 'radps' + time = self.time + load = loads.ConstLoad(ltype, comp, radps) + self.__add_load(load, time) + return load
+ +
[docs] def set_fluid_press(self, items, rho, g, xo, po): + """This sets a fluid presure load on items. + + This fluid pressure is dependednt on the x axis. + g must be positive. + + - P = f(x) + - P(xo) = po + - P(x) = po + rho*g*(xo - x) + + Args: + items (str or Line or Arc or list): items to set pressure on + + - str: string name of Line or Arc item, for example 'L0' + - Line or Arc: set presssure on this + - list or Line or Arc: set pressure on these + rho (float): fluid density in mass/volume + g (+float): gravity in dist/(t^2), MUST BE POSITIVE + xo (float): sea level height, MUST BE POSITIVE + po (float): sea level pressure, MUST BE POSITIVE + """ + items = self.get_items(items) + ctype = 'faces' + cname = self.__get_cname(items) + + # make compoenet + comp = components.Component(items, ctype, cname) + comp = self.__get_make_comp(comp) + + # make load + ltype = 'press_fluid' + mult = rho*g + load = loads.LinearLoad(ltype, comp, po, mult, xo) + self.__add_load(load, self.time) + return load
+ +
[docs] def set_load(self, ltype, items, lval, ldir=None): + """Sets a pressure or force load on line(s). + + The force load is divided by the number of nodes and applied on + each node. + + The pressure load is applied to the child faces under the line. + Positive is compression, negative is tension. + + Args: + ltype (str): 'press' or 'force' + items (str or Line or Arc or list): items to set load on + + - str: string name of Line or Arc item, for example 'L0' + - Line or Arc: set load on this + - list or Line or Arc: set load on these + lval (float): load value. + + - For ltype = 'press' this is in force/area units + - For ltype = 'force' this is in force units + ldir (None or str): load direction. Defaults to None + + - str: when ltype='load', we need to set ldir to 'x' or 'y' + - None: when ltype='press' + """ + items = self.get_items(items) + ctype = 'nodes' + if ltype == 'press': + ctype = 'faces' + cname = self.__get_cname(items) + + # make compoenet + comp = components.Component(items, ctype, cname) + comp = self.__get_make_comp(comp) + + # make load + if ltype == 'force': + ltype = 'f'+ldir # for example fx + load = loads.ConstLoad(ltype, comp, lval) + self.__add_load(load, self.time) + return load
+ +
[docs] def set_constr(self, ltype, items, axis, val=0.0): + """Sets a displacement constraint on the passed item(s). + + Args: + ltype (str): 'fix' or 'displ' + + - 'fix': val arg should not be passed + - 'displ': val arg must be passed + items (str, SignLine, SignArc, Point or list): item(s) to apply the + constraint on + + - str: this string must be a line or point name + - SignLine or SignArc: the constraint will be applied + to this item + - Point: the constraint will be applied on this item + - list of SignLine or SignArc: the constraint will be appplied + on these + - list of Point: the constraint will be appplied on these + - list of str names: pass line or point names, constr + applied on them + + axis (str): load axis, 'x' or 'y' + val (float): displacement value, defaults to 0.0, only needs to be + set when ltype = 'displ' + """ + items = self.get_items(items) + cname = self.__get_cname(items) + ctype = 'nodes' + + # make compoenet + comp = components.Component(items, ctype, cname) + comp = self.__get_make_comp(comp) + + # make load + ltype = 'u'+axis # for example ux + load = loads.ConstLoad(ltype, comp, val) + self.__add_load(load, self.time) + return load
+ +
[docs] def set_contact_linear(self, master_lines, slave_lines, kval, many_si=False): + """Sets contact between master and slave lines. + + Slave lines are on the more compliant or more curved object. + + Args: + master_lines (list): list of SignLine or SignArc + slave_lines (list): list of SignLine or SignArc + kval (float): stiffness, 5 to 50 times the youngs modulus + of the touching matl + many_si (bool): True, make unique surface interaction for every contact + False, use existing surface interaction if we can + """ + + master_items = self.get_items(master_lines) + master_cname = self.__get_cname(master_items) + ctype = 'faces' + master_comp = components.Component(master_items, ctype, master_cname) + master_comp = self.__get_make_comp(master_comp) + + slave_items = self.get_items(slave_lines) + slave_cname = self.__get_cname(slave_items) + ctype = 'faces' + slave_comp = components.Component(slave_items, ctype, slave_cname) + slave_comp = self.__get_make_comp(slave_comp) + + surf_int = connectors.SurfaceInteraction('LINEAR', kval) + if many_si: + surf_int = self.surfints.append(surf_int) + else: + surf_int = self.__get_make_surfint(surf_int) + + cont = connectors.Contact(master_comp, slave_comp, surf_int, True) + self.contacts.append(cont)
+ + +
[docs] def set_eshape(self, eshape='quad', eorder=2): + """Sets the element shape and order to use when meshing the model. + + Args: + eshape (str): element shape + + - 'quad': quadrilaterials + - 'tri': triangles + eorder (int): element order, default=2 + + - 1: corner nodes only (3 and 4 noded elements) + - 2: corder nodes and midside nodes (6 and 8 noded elements) + """ + self.eshape = eshape # quad or tri + self.eorder = eorder # 1 or 2
+ +
[docs] def set_etype(self, etype, items, thick=None): + """Sets the element type, and thickness on areas or parts. + + Args: + etype (str): element type + + - 'plstress': plane stress + - 'plstrain': plane strain + - 'axisym': axisymmetric + items (str or Area or Part or list): set element type on these + + - str: string name of Area or Part item. Example: 'A0', 'PART0' + - Area: set element type on the elements in this area + - Part: set element type on the elements in this part + - list of Area or Part: set element type on their elements + thick (float or None): element thickness + + - None: default, used for axisymmetric element type + - float: thickness value to use for plane stress or plane + strain elements + """ + items = self.get_items(items) + + # set the element types on the areas, this is used to fix + # elements when importing them from the inp file + for item in items: + if isinstance(item, geometry.Area): + item.set_etype(etype) + if isinstance(item, partmodule.Part): + for area in item.areas: + area.set_etype(etype) + + # set a thickness component if needed + if etype != 'axisym' and thick != None: + + # make component for element thickness + cname = self.__get_cname(items) + ctype = 'nodes' + comp = components.Component(items, ctype, cname) + comp = self.__get_make_comp(comp) + + # add load + ltype = 'nodal_thickness' + time = 0.0 + load = loads.ConstLoad(ltype, comp, thick) + self.__add_load(load, time) + return load + return None
+ +
[docs] def set_matl(self, matl, items): + """Sets the material on Part or Area items. + + Args: + matl (Material): material to assign to items + items (Part or Area or list): items that will have matl assigned + + - Part: assign matl to this + - Area: assign matl to this + - list of Area or Part: assign matl to these + """ + items = self.get_items(items) + cname = self.__get_cname(items) + + # store the material if it's not already + if matl not in self.matls: + matl = self.matls.append(matl) + + # make component + ctype = 'elements' + comp = components.Component(items, ctype, cname) + comp = self.__get_make_comp(comp) + + # make load + ltype = 'matl' + time = 0.0 + load = loads.ConstLoad(ltype, comp, matl) + self.__add_load(load, time) + return load
+ + def __read_inp(self, fname): + """Reads in the mesh from a Calculix inp file. + + All nodes, elements, and faces are read. Child nodes, elements and + faces are assigned to their geometry parents (points, lines, arcs, + areas, parts. + + Args: + fname (str): file name to read, must include '.inp' extension + """ + f = open(fname, 'r') + mode = None + set_name = None + set_type = None + + items = [] # holder for nodes or elements in nsets or esets + N = base_classes.Meshlist() # store nodes + E = base_classes.Meshlist() # store elements, allows renumbering before putting int model + F = [] # store faces + sets = {'E':{}, 'N':{}} # store sets + etype = '' + Dict_NodeIDs={} + Dict_ElemIDs={} + + # read in input file + for line in f: + if line[0] != '*': + if mode == 'nmake': + L = line.split(',') + L = [a.strip() for a in L] + (nnum, x, y, z) = (int(L[0]), float(L[1]), float(L[2]), float(L[3])) + node = mesh.Node(nnum, x, y, z) + N.append(node) + Dict_NodeIDs[nnum]=node + elif mode == 'emake': + L = line.split(',') + L = [int(a.strip()) for a in L] + enum = L[0] + nlist = [Dict_NodeIDs[a] for a in L[1:]] + e = mesh.Element(enum, etype, nlist) + faces = e.faces + E.append(e) + Dict_ElemIDs[enum]=e + F += faces + sets[set_type][set_name].append(e) + elif mode == 'set': + L = line.split(',') + L = [a.strip() for a in L] + L = [int(a) for a in L if a != ''] + items = [] + if set_type == 'E': + items = [Dict_ElemIDs[a] for a in L if a in Dict_ElemIDs.keys()] + elif set_type == 'N': + items = [Dict_NodeIDs[a] for a in L] + if items == [None]*len(items): + pass # the elements were not found + else: + sets[set_type][set_name] += items + + # mode setting + if '*Node' in line or '*NODE' in line: + mode = 'nmake' + elif '*Element' in line or '*ELEMENT' in line: + L = line.split(',') # split it based on commas + e = L[1].split('=') + etype = e[1] + + # exclude T elements made in gmsh + if etype[0] != 'T': + e = L[2].split('=') + set_name = e[1].strip() + set_type = 'E' + sets[set_type][set_name] = [] + mode = 'emake' + else: + mode = None + elif '*ELSET' in line: + L = line.split(',') + e = L[1].split('=') + set_name = e[1].strip() + set_type = 'E' + sets[set_type][set_name] = [] + mode = 'set' + elif '*NSET' in line: + L = line.split(',') + e = L[1].split('=') + set_name = e[1].strip() + set_type = 'N' + sets[set_type][set_name] = [] + mode = 'set' + f.close() + + # loop through sets and remove empty sets + # store sets to delete + todel = [] + for (set_type, set_dict) in sets.items(): + for (set_name, item_list) in set_dict.items(): + if item_list == []: + todel.append({'set_type':set_type, 'set_name':set_name}) + # delete the empty sets + for adict in todel: + (set_type, set_name) = (adict['set_type'], adict['set_name']) + del sets[set_type][set_name] + #print('Empty set type:%s name:%s deleted' % (set_type, set_name)) + + # this resets the min element to number 1 + if E.get_minid() > 1: + E.set_minid(1) + + #----------------------------------- + # Node and element assignment back onto parts, areas, lines, points + #----------------------------------- + # assign elements + nodes to feamodel + self.elements = E + self.faces = F + + # remove arc center nodes from imported node set + # those nodes have no elements under them + torem = [] + for node in N: + if len(node.elements) == 0: + torem.append(node) + for node in torem: + N.remove(node) + # assign nodes to feamodel + self.nodes = N + + for part in self.parts: + # assign part element and node sets + pname = part.get_name() + part.elements = sets['E'][pname] + part.nodes = sets['N'][pname] + + # assign all nodes and elements to areas, fix the element types + for area in part.areas: + aname = area.get_name() + area.elements = sets['E'][aname] + area.nodes = sets['N'][aname] + area.set_child_ccxtypes() #paint element types on elements + + # assign the child nodes to points + pts = part.points + for point in pts: + ndist = [] + for node in part.nodes: + p_tmp = geometry.Point(node.x, node.y) + dist = point - p_tmp + dist = dist.length() + ndist.append({'dist':dist, 'node':node}) + # sort the list by dist, sorts low to high + ndist = sorted(ndist, key=lambda k: k['dist']) + point.nodes = [ndist[0]['node']] + #print('Point %s = node %s' % (pt, pt.nodes)) + + # assign the nodes and n1 and faces to lines + slines = part.signlines + for sline in slines: + lname = sline.get_name() + area = sline.lineloop.parent + nodes = sets['N'][sline.line.get_name()] + n1 = [n for n in nodes if n.order == 1] + sline.nodes = nodes + sline.n1 = n1 + # make a set of all faces that contain the line's node + allfaces = set() + for node in nodes: + allfaces.update(node.faces) + # make a set of all faces on the line only + linefaces = set() + for face in allfaces: + if set(face.nodes).issubset(set(nodes)): + linefaces.add(face) + # and only the ones whose element centers are in the area + faces = set() + for face in linefaces: + is_lineface = area.contains_point(face.element.center) + if is_lineface: + faces.add(face) + sline.faces = list(faces) + print('Elements: %i' % len(E)) + print('Nodes: %i' % len(N)) + print('Done reading Calculix/Abaqus .inp file') + +
[docs] def mesh(self, size=1.0, meshmode='fineness', mesher='gmsh'): + """Meshes all parts. + + Args: + size (float): + + - if meshmode == 'fineness' (default): + - mesh size is adapted to geometry size + - set size = 0.0001 - 1.0, to define how fine the mesh is. + - Low numbers are very fine, higher numbers are coarser. + + - if meshmode == 'esize': + - element size is kept constant + - choose it depending on geometry size + - it should be reduced e.g. at arcs with small radius, by calling line.esize function + + meshmode (str): + + - 'fineness': adapt mesh size to geometry + - 'esize': keep explicitly defined element size + + meshmode is changed to 'esize' is used if esize property is set to points or lines + + mesher (str): the mesher to use + + - 'gmsh': mesh with Gmsh, this is reccomended, it allows holes + - 'cgx': mesh with Calculix cgx, it doesn't allow holes + """ + + + #check if element size is set to points and change meshmode if necessary + for pt in self.points: + if pt.esize != None: + if meshmode=='fineness': print('meshmode is changed to esize, because elementsize was defined on points!') + meshmode = 'esize' + + + #if meshmode esize is chosen: ediv's on lines and arcs are transformed to element sizes on start and end point + if meshmode == 'esize': + for line in self.lines: + if line.ediv != None: + line.pt(0).set_esize(line.length()/line.ediv) + line.pt(1).set_esize(line.length()/line.ediv) + + + if mesher == 'gmsh': + self.__mesh_gmsh(size, meshmode) + elif mesher == 'cgx': + self.__mesh_cgx(size)
+ + def __mesh_gmsh(self, size, meshmode, timeout=20): + """Meshes all parts using the Gmsh mesher. + + Args: + + size (float): + + - if meshmode == 'fineness' (default): + - mesh size is adapted to geometry size + - set size = 0.0001 - 1.0, to define how fine the mesh is. + - Low numbers are very fine, higher numbers are coarser. + + - if meshmode == 'esize': + - element size is kept constant + - choose it depending on geometry size + + meshmode (str): + + - 'fineness': adapt mesh size to geometry + - 'esize': keep explicitly defined element size + + timeout (int): time in seconds before the process throws a + subprocess.TimeoutExpired + + """ + geo = [] + ids = {} + ids['line'] = {} + ids['plane_surface'] = {} + + # write all points + for pt in self.points: + txtline = 'Point(%i) = {%f, %f, %f};' % (pt.id, pt.x, pt.y, 0.0) + + if meshmode == 'esize': + #add element size to points + if pt.esize == None: + txtline = txtline.replace('}', ', %f}' % (size if self.eshape=='tri' else size*2.)) + #txtline = txtline.replace('}', ', %f}' % (size)) + else: + txtline = txtline.replace('}', ', %f}' % (pt.esize if self.eshape=='tri' else pt.esize*2.)) + #txtline = txtline.replace('}', ', %f}' % (pt.esize)) + geo.append(txtline) + + # start storing an index number + ind = self.points[-1].id + 1 + + # write all lines + for line in self.lines: + lnum = line.id + ids['line'][lnum] = ind + pt1 = line.pt(0).id + pt2 = line.pt(1).id + txtline = '' + if isinstance(line, geometry.Arc): + ctr = line.actr.id + txtline = 'Circle(%i) = {%i, %i, %i};' % (ind, pt1, ctr, pt2) + else: + txtline = 'Line(%i) = {%i,%i};' % (ind, pt1, pt2) + geo.append(txtline) + + # set division if we have it + if line.ediv != None and meshmode=='fineness': + ndiv = line.ediv+1 + esize = line.length()/line.ediv + if self.eshape == 'quad': + ndiv = line.ediv/2+1 + esize = esize*2 + # this is needed because quad recombine + # splits 1 element into 2 + txtline = 'Transfinite Line{%i} = %i;' % (ind, ndiv) + print('LINE ELEMENT SIZE: %f, MAKES %i ELEMENTS' + % (line.length()/line.ediv, line.ediv)) + geo.append(txtline) + geo.append('Characteristic Length {%i,%i} = %f;' + % (pt1, pt2, esize)) + ind += 1 + + # write all areas + for area in self.areas: + if area.closed: + aid = area.id + aname = area.get_name() + loop_ids = [] + loops = [area.exlines] + area.holes + for loop in loops: + txtline = 'Line Loop(%i) = ' % (ind) + loop_ids.append(str(ind)) + line_ids = [] + for sline in loop: + lid = ids['line'][sline.line.id] + prefix = '' + if sline.sign == -1: + prefix = '-' + line_ids.append('%s%i' % (prefix, lid)) + txtline = txtline + '{'+ ','.join(line_ids)+'};' + geo.append(txtline) + ind += 1 + loop_ids = ','.join(loop_ids) + geo.append('Plane Surface(%i) = {%s};' % (ind, loop_ids)) + ids['plane_surface'][aid] = ind + geo.append("Physical Surface('%s') = {%i};" % (aname, ind)) + ind += 1 + + # write part area components + for part in self.parts: + # make components for each part + txtline = "Physical Surface('%s') = " % (part.get_name()) + area_ids = [] + for area in part.areas: + if area.closed: + aid = ids['plane_surface'][area.id] + area_ids.append(str(aid)) + txtline = txtline + '{' + ','.join(area_ids) + '};' + geo.append(txtline) + + # write all line componenets so we can get nodes out + for line in self.lines: + lid = ids['line'][line.id] + txtline = "Physical Line('%s') = {%i};" % (line.get_name(), lid) + geo.append(txtline) + + # write node componenets + # ERROR: node list is not produced by gmsh + for point in self.points: + pname = point.get_name() + txtline = "Physical Point('%s') = {%i};" % (pname, point.id) + geo.append(txtline) + + # set the meshing options + if meshmode == 'fineness': + geo.append('Mesh.CharacteristicLengthFactor = '+str(size)+'; //mesh fineness') + geo.append('Mesh.RecombinationAlgorithm = 1; //blossom') + + if self.eshape == 'quad': + geo.append('Mesh.RecombineAll = 1; //turns on quads') + geo.append('Mesh.SubdivisionAlgorithm = 1; // quadrangles only') + #geo.append('Mesh.RecombinationAlgorithm = 1; //turns on blossom needed for quad') + + order = self.eorder + geo.append('Mesh.CharacteristicLengthExtendFromBoundary = 1;') + geo.append('Mesh.CharacteristicLengthMin = 0;') + geo.append('Mesh.CharacteristicLengthMax = 1e+022;') + # use this so small circles are meshed finely + # this is broken in Gmsh 4.3.0 + # geo.append('Mesh.CharacteristicLengthFromCurvature = 1;') + geo.append('Mesh.CharacteristicLengthFromPoints = 1;') + # geo.append('Mesh.Algorithm = 2; //delauny') #okay for quads + geo.append('Mesh.Algorithm = 8; //delquad = delauny for quads') + geo.append('Mesh.ElementOrder = ' + +str(order) + +'; //linear or second set here') + if order == 2: + geo.append('Mesh.SecondOrderIncomplete=1; //no face node w/ 2nd order') + geo.append('Mesh.SaveGroupsOfNodes = 1; // save node groups') + + # write geo file to the local directory + fname = self.fname+'.geo' + fout = self.fname+'.inp' + outfile = open(fname, 'w') + for line in geo: + #print (line) + outfile.write(line+'\n') + outfile.close() + print('File: %s was written' % fname) + + # run file in bg mode, -2 is 2d mesh, makes required inp file + runstr = "%s %s -2 -o %s" % (environment.GMSH, fname, fout) + print(runstr) + subprocess.check_call(runstr, timeout=timeout, shell=True) + print('File: %s was written' % fout) + print('Meshing done!') + + # write gmsh msh file, for manual checking only + # not required by pycalculix + runstr = "%s %s -2 -o %s" % (environment.GMSH, fname, + self.fname+'.msh') + subprocess.check_call(runstr, timeout=timeout, shell=True) + print('File: %s.msh was written' % self.fname) + + # read in the calculix mesh + self.__read_inp(self.fname+'.inp') + + def __mesh_cgx(self, size, meshmode, timeout=20): + """Meshes all parts using the Calculix cgx mesher. + + Args: + + size (float): + + - if meshmode == 'fineness' (default): + - mesh size is adapted to geometry size + - set size = 0.0001 - 1.0, to define how fine the mesh is. + - Low numbers are very fine, higher numbers are coarser. + + - if meshmode == 'esize': NOT TESTED WITH CGX + - element size is kept constant + - choose it depending on geometry size + + meshmode (str): + + - 'fineness': adapt mesh size to geometry + - 'esize': keep explicitly defined element size NOT TESTED WITH CGX + + timeout (int): time in seconds before the process throws a + subprocess.TimeoutExpired + """ + fbd = [] + comps = [] + cfiles = [] + + # Calculix CGX elements + # axisymmetric + cgx_elements = {} + cgx_elements['tri2axisym'] = 'tr6c' + cgx_elements['tri1axisym'] = 'tr3c' + cgx_elements['quad2axisym'] = 'qu8c' + cgx_elements['quad1axisym'] = 'qu4c' + # plane stress + cgx_elements['tri2plstress'] = 'tr6s' + cgx_elements['tri1plstress'] = 'tr3s' + cgx_elements['quad2plstress'] = 'qu8s' + cgx_elements['quad1plstress'] = 'qu4s' + # plane strain + cgx_elements['tri2plstrain'] = 'tr6e' + cgx_elements['tri1plstrain'] = 'tr3e' + cgx_elements['quad2plstrain'] = 'qu8e' + cgx_elements['quad1plstrain'] = 'qu4e' + + num = 1.0/size + emult = int(round(num)) # this converts size to mesh multiplier + + # write all points + for point in self.points: + linestr = 'pnt %s %f %f %f' % (point.get_name(), point.x, point.y, 0.0) + fbd.append(linestr) + # gmsh can't make node componenets so don't do it in cgx + #L = 'seta %s p %s' % (pt.get_name(), pt.get_name()) + #comps.append(L) + + # write all lines + for line in self.lines: + lname = line.get_name() + pt1 = line.pt(0).get_name() + pt2 = line.pt(1).get_name() + linestr = '' + if isinstance(line, geometry.Arc): + # line is arc + pctr = line.actr.get_name() + linestr = 'line %s %s %s %s' % (lname, pt1, pt2, pctr) + else: + # straight line + linestr = 'line %s %s %s' % (lname, pt1, pt2) + # set division if we have it + if line.ediv != None: + ndiv = self.eorder*line.ediv + linestr += ' '+str(int(ndiv)) + fbd.append(linestr) + linestr = 'seta %s l %s' % (lname, lname) + comps.append(linestr) + cfiles.append(lname) + + # write all areas + for area in self.areas: + if area.closed: + linestr = 'gsur '+area.get_name()+' + BLEND ' + line_ids = [] + for line in area.signlines: + lname = '+ '+line.get_name() + if line.sign == -1: + lname = '- '+line.get_name()[1:] + line_ids.append(lname) + linestr = linestr + ' '.join(line_ids) + fbd.append(linestr) + # add area component, nodes + elements + astr = 'seta %s s %s' % (area.get_name(), area.get_name()) + fbd.append(astr) + cfiles.append(area.get_name()) + + # write part area components + for apart in self.parts: + # make components for each part + # seta P0 s s0 s1 + line = 'seta %s s ' % apart.get_name() + cfiles.append(apart.get_name()) + area_ids = [] + for area in apart.areas: + if area.closed: + area_ids.append(area.get_name()) + line = line + ' '.join(area_ids) + fbd.append(line) + + # mesh all areas + for area in apart.areas: + aname = area.get_name() + estr = self.eshape+str(self.eorder)+area.etype + cgx_etype = cgx_elements[estr] + fbd.append('elty %s %s' % (aname, cgx_etype)) + fbd.append('div all mult %i' % emult) + fbd.append('mesh all') + + # save mesh file + fbd.append('send all abq') + + # add line and area components + fbd += comps + + # save component node and element sets + for comp in cfiles: + # select nodes under + fbd.append('comp %s do' % (comp,)) + fbd.append('send %s abq names' % (comp,)) + + # this orients the view correctly + # this is the same as switching to the z+ orientation + # y us axial, x is radial + fbd.append('rot z') + fbd.append('rot c -90') + fbd.append('plot e all') + + # write fbd file to the local directory + fname = self.fname+'.fbd' + f = open(fname, 'w') + for line in fbd: + #print (line) + f.write(line+'\n') + f.close() + print('File: %s was written' % fname) + + # run file in bg mode + runstr = "%s -bg %s" % (environment.CGX, fname) + p = subprocess.check_call(runstr, timeout=timeout, shell=True) + print('Meshing done!') + + # assemble the output files into a ccx input file + inp = [] + files = ['all.msh'] + files += [f+'.nam' for f in cfiles] + for fname in files: + infile = open(fname, 'r') + for line in infile: + # cgx adds E and N prfixes on sets after =, get rid of these + if '=' in line and fname != 'all.msh': + L = line.split('=') + line = L[0] + '=' + L[1][1:] + inp.append(line.strip()) + else: + inp.append(line.strip()) + infile.close() + + # delete file + os.remove(fname) + + # write out inp file + fname = self.fname+'.inp' + outfile = open(fname, 'w') + for line in inp: + #print (line) + outfile.write(line+'\n') + outfile.close() + print('File: %s was written' % fname) + + # read in the calculix mesh + self.__read_inp(fname)
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/geometry.html b/dist/documentation/_modules/pycalculix/geometry.html new file mode 100644 index 0000000..43e8d95 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/geometry.html @@ -0,0 +1,2150 @@ + + + + + + + pycalculix.geometry — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.geometry

+"""This module stores geometry classes, which are used to make parts.
+"""
+from math import atan2, pi, cos, sin, radians, degrees
+from matplotlib.patches import Arc as AArc
+from matplotlib.patches import PathPatch
+from matplotlib.path import Path
+from numpy.linalg import det
+from numpy import array
+from numpy.linalg import solve as linsolve
+
+from . import base_classes
+
+#accuracy for small numbers in math below
+ACC = .00001 # original
+
+LDIVS = {}
+
+
[docs]def get_text_hv(angle): + """Returns (ha, va) text horizontal and vertical alignment for line label. + + This makes the line label text inside its area, assuming areas closed CW. + + Args: + angle (float): line normal vector in degrees, between 180 and -180 + """ + horiz, vert = ('', '') + if abs(angle) < 67.5: + horiz = 'right' + elif 67.5 <= abs(angle) < 112.5: + horiz = 'center' + elif 112.5 <= abs(angle) <= 180: + horiz = 'left' + if abs(angle) < 22.5 or abs(angle) > 157.5: + vert = 'center' + else: + if angle > 0: + vert = 'top' + else: + vert = 'bottom' + return (horiz, vert)
+ + +
[docs]class Point(base_classes.Idobj): + """Makes a point or vector. + + Args: + x (float): vertical coordinate of point + y (float): horizontal coordinate of point + z (float): in-page coordinate of point, defaults to zero + + Attributes: + x (float): vertical coordinate of point + y (float): horizontal coordinate of point + z (float): in-page coordinate of point, defaults to zero + nodes (Node): a list of nodes in a mesh at this point. + List length is 1. + nodes (list): list of nodes under point + lines (set): set of lines which use this point + arc_center (bool): True if this point is an arc center + """ + + def __init__(self, x, y, z=0): + self.x = x + self.y = y + self.z = z + self.nodes = [] + self.lines = set() + self.arc_center = False + self.on_part = True + self.esize = None + base_classes.Idobj.__init__(self) + +
[docs] def get_name(self): + """Returns item name.""" + return 'P'+str(self.id)
+ + def __eq__(self, other): + """Checks x and y equality to other point. + + Args: + other (Point): passed point + """ + if isinstance(other, Point): + if self.x == other.x and self.y == other.y: + return True + return False + else: + return False + + def __add__(self, other): + """Returns new point = self + other. + + Args: + other (Point): passed point + + Returns: + Point = self + other (new point, does not modify self) + """ + return Point(self.x+other.x, self.y+other.y, self.z+other.z) + + def __sub__(self, other): + """Returns new point = self - other. + + Args: + other (Point): passed point + + Returns: + Point = self - other (new point, does not modify self) + """ + return Point(self.x-other.x, self.y-other.y, self.z-other.z) + + def __hash__(self): + """Set the hash of the item to the id number. + + This allows one to make sets of these items. + """ + return self.id + + def __mul__(self, factor): + """Returns new point = self*factor. + + Args: + factor (float): factor to multiply the Point by + + Returns: + Point = self*factor (new point, does not modify self) + """ + return Point(self.x*factor, self.y*factor, self.z*factor) + + def __truediv__(self, other): + """Returns new point = self/other. + + Args: + other (float or Point): item we're dividing by + + Returns: + Point or factor. Does not modify self. + """ + if isinstance(other, float): + return Point(self.x/other, self.y/other, self.z/other) + elif isinstance(other, Point): + res = [] + if other.x != 0: + res.append(self.x/other.x) + if other.y != 0: + res.append(self.y/other.y) + if other.z != 0: + res.append(self.z/other.z) + factor = sum(res)/len(res) + return factor + +
[docs] def set_line(self, line): + """Saves the line or arc in the point.""" + if line not in self.lines: + self.lines.add(line) + if self.arc_center == True: + if len(self.lines) > 1: + for line in self.lines: + if isinstance(line, Line): + self.arc_center = False + break
+ +
[docs] def unset_line(self, line): + """Removes the line or arc in the point.""" + if line not in self.lines: + return + self.lines.remove(line)
+ +
[docs] def length(self): + """Returns the length of this point or vector.""" + res = (self.x**2 + self.y**2)**0.5 + return res
+ +
[docs] def label(self, axis): + """Labels the point on a Matplotlib axis. + + Args: + axis (Matplotlib Axis): Matplotlib Axis + """ + axis.annotate(self.get_name(), (self.y, self.x))
+ +
[docs] def plot(self, axis, label=True): + """Plots the point on the passed matplotlib axis. + + Args: + axis (Matplotlib axis): plate to plot the point + pnum (bool): True turns on point labeling + """ + axis.scatter(self.y, self.x) + if label: + self.label(axis)
+ +
[docs] def make_unit(self): + """Modifies self vector to make it a unit vector.""" + lenval = self.length() + self.x = self.x*1/lenval + self.y = self.y*1/lenval
+ +
[docs] def ang_rad(self): + """Returns angle in radians, assume point is vector from 0,0.""" + vert = self.x + horiz = self.y + rad_angle = atan2(vert, horiz) + return rad_angle
+ +
[docs] def ang_deg(self): + """Returns angle in degrees, assume point is vector from 0,0.""" + rad_angle = self.ang_rad() + deg = rad_angle * 180.0 / pi + return deg
+ +
[docs] def axrad(self): + """Returns a tuple of (ax, rad) for plotting.""" + return (self.y, self.x)
+ +
[docs] def radax(self): + """Returns a tuple of (rad, ax) for checking if inside.""" + return (self.x, self.y)
+ +
[docs] def rot_ccw_deg(self, ang): + """Rotates the current vector by ccw degrees about 0,0. Returns self. + + Args: + ang (float): angle or rotation in degrees, counter-clockwise (ccw) + """ + ang = radians(ang) + axial = self.y*cos(ang) - self.x*sin(ang) + rad = self.y*sin(ang) + self.x*cos(ang) + axial = round(axial, 5) + rad = round(rad, 5) + (self.x, self.y) = (rad, axial) + return self
+ +
[docs] def ang_bet_rad(self, other): + """Returns angle between self and other vector in radians. + + Assumes vectors start at 0,0 + + Args: + other (Point): passed vector + + Returns: + angle (float): radians between self vector and other vector + """ + avect = Point(self.y*other.x - self.x*other.y, self.y*other.y + self.x*other.x) + ang = avect.ang_rad() + return ang
+ +
[docs] def ang_bet_deg(self, other): + """Returns angle between self and other vector in degrees. + + Assumes vectors start at 0,0 + + Args: + other (Point): passed vector + + Returns: + angle (float): degrees between self vector and other vector + """ + avect = Point(self.y*other.x - self.x*other.y, self.y*other.y + self.x*other.x) + ang = avect.ang_deg() + return ang
+ + def __str__(self): + """Returns string listing object type, id number, and coordinates""" + val = 'Point %s, (x, y)=(%.3f, %.3f)' % (self.get_name(), self.x, self.y) + return val + +
[docs] def set_esize(self, size): + self.esize = size
+ + + +
[docs]class Line(base_classes.Idobj): + """Stores a line from points start_pt to end_pt. + + Args: + start_pt (Point): first point + end_pt (Point): second point + + Attributes: + points (list of Point) : list of the line's points [start_pt, end_pt] + ediv (None or float): number of elements on the line + midpt (Point): a mid-point between start_pt and end_pt + nodes (list of Node): a list of meshed nodes on the line + edge (bool): True if line is a non-shared edge in an area + signlines (list): list of SignLine that use this Line + """ + + def __init__(self, start_pt, end_pt): + self.points = [start_pt, end_pt] + self.midpt = self.mid() + self.ediv = None + self.nodes = [] + self.edge = True + self.signlines = [] + base_classes.Idobj.__init__(self) + +
[docs] def reverse(self): + """Reverses self.""" + self.points.reverse()
+ + @property + def allpoints(self): + """Returns all line defining points.""" + return self.points + + @property + def areas(self): + """Returns all areas that signlines use.""" + areas = set() + for sline in self.signlines: + areas.add(sline.parent) + return list(areas) + + def __eq__(self, other): + """Returns bool telling if self == other. + + Returns True if the lines are the same type, and contain the same + list of points. + """ + if isinstance(other, self.__class__): + mypts = self.points + otherpts = other.points + same = True + for (mypt, otherpt) in zip(mypts, otherpts): + if mypt != otherpt: + same = False + break + return same + else: + return False + + def __hash__(self): + """Set the hash of the item to the id number. + + This allows one to make sets of these items. + """ + return self.id + +
[docs] def add_signline(self, signline): + """Adds a signline to self.signlines.""" + if signline not in self.signlines: + self.signlines.append(signline)
+ +
[docs] def get_name(self): + """Returns the line name.""" + return 'L'+str(self.id)
+ +
[docs] def length(self): + """Returns the length of the line.""" + return (self.pt(0)-self.pt(1)).length()
+ +
[docs] def save_to_points(self): + """This stores this line in the line's child points.""" + for point in self.allpoints: + point.set_line(self)
+ +
[docs] def set_ediv(self, ediv): + """Sets the number of element divisions on the line when meshing. + + Args: + ediv (int): number of required elements on this line + """ + self.ediv = ediv
+ +
[docs] def set_esize(self, esize): + """Sets the size of mesh elements on the line when meshing. + + Args: + esize (float): size of mesh elements on this line + """ + for i in range(2): + self.pt(i).set_esize(esize)
+ +
[docs] def signed_copy(self, sign): + """Returns a SignLine copy of this Line with the passed sign.""" + return SignLine(self, sign)
+ +
[docs] def pt(self, ind): + """Returns the start or end point of the line. + + Args: + ind (int): index of the point we want, 0=start, 1=end + + Returns: + Point: requested Point + """ + return self.points[ind]
+ +
[docs] def set_pt(self, ind, point): + """Update the line's point at ind to the new point. + + Args: + ind (int): index of the point we're updating, 0=start, 1=end + point (Point): new point we are assigning to Line + """ + # remove this line from the old point + self.points[ind].lines.remove(self) + # update the point + self.points[ind] = point + self.midpt = self.mid() + self.save_to_points() + other_point = self.pt(int(not ind)) + if point == other_point: + # we are making a zero length line + other_point.lines.remove(self) + if self.id != -1: + if len(self.signlines) > 0: + area = self.signlines[0].lineloop.parent + area.part.fea.lines.remove(self) + for sline in self.signlines: + sline.lineloop.remove(sline)
+ +
[docs] def mid(self): + """Calculates and returns a midpoint between start and end points.""" + point = (self.pt(0) + self.pt(1))*0.5 + return point
+ +
[docs] def touches(self, other): + """Checks if a line is connected to a passed line. + + Args: + other (Line or Arc): passed other line + + Returns: + True: beginning or end point connect to other line + False: lines are not connected + """ + [start_pt, end_pt] = [self.pt(0), self.pt(1)] + if start_pt in other.points or end_pt in other.points: + return True + else: + return False
+ +
[docs] def offset(self, dist): + """Returns a new line offset from the dirrent line by value dist. + + Right hand rule is followed assuming x is the origina line. + Z points out of page. + Y is the direction of line offset. + + Args: + dist (float): distance to offset the new line by + + Returns: + tmpline (Line): new Line which is offset from self by dist + """ + lvect = self.get_perp_vec() + lvect.make_unit() + # scale it by the new amount + lvect = lvect*dist + # add the vector onto the defining points + start_pt = self.pt(0) + lvect + end_pt = self.pt(1) + lvect + tmpline = Line(start_pt, end_pt) + return tmpline
+ +
[docs] def get_abc(self): + """ Returns a list of abc terms for a line definition. + + ax + by + c = 0 + + Returns: + [a, b, c]: list of the above line terms + """ + delta = self.pt(1)-self.pt(0) + dx_ = delta.x + dy_ = delta.y + (a__, b__, c__) = (0.0, 0.0, 0.0) + if dy_ == 0: + # vertical radial line + # y = 5 --> 0 = -y + 5 --> 0 = 0x -y + 5 + (b__, c__) = (-1.0, self.pt(0).y) + elif dx_ == 0: + # horizontal axial line + # x = 5 --> 0 = -x + 5 --> 0 = -x +0y + 5 + (a__, c__) = (-1.0, self.pt(0).x) + else: + # y = ax + c --> 0 = ax - y + c + slope = dy_/dx_ + offset = self.pt(0).y - slope*self.pt(0).x + (a__, b__, c__) = (slope, -1.0, offset) + return [a__, b__, c__]
+ +
[docs] def get_perp_vec(self, point=None): + """ Returns vector perpendicular to current line. + + Vector is created by rotating the current line ccw 90 degrees + around the start point. + + Args: + point (Point): defaults to None, unused for Line, used for Arc + + Returns: + lvect (Point): vector that is perpendiculat to the current line + """ + lvect = self.pt(1) - self.pt(0) + lvect.rot_ccw_deg(90) + return lvect
+ +
[docs] def get_tan_vec(self, point): + """ Returns vector tangent to the current line. + + Vector points from the passed point to a unit location further away. + + Args: + point (Point): start point + + Returns: + lvect (Point): vector that is tangent to the current line + """ + end = self.pt(0) + if point == end: + end = self.pt(1) + lvect = end - point + lvect.make_unit() + return lvect
+ +
[docs] def arc_tang_intersection(self, point, mag): + """Returns an intersection point on this line of an arc at center point + + Args: + point (Point): arc center point + mag (float): passed radius distance from the arc center to the line + + Returns: + newpt (Point or None): + Point: the intersection point + None: returns None if no intersection point exists + """ + vect = self.get_perp_vec() + vect.make_unit() + pt2 = point + vect*(-2*mag) + + tmpline = Line(point, pt2) + newpt = tmpline.intersects(self) + if type(newpt) == type(None): + print('Intersection failed!') + return newpt
+ +
[docs] def coincident(self, point): + """Checks to see if point is on the line. + + Args: + point (Point): input point to check + + Returns: + bool: True if point on line or False if not + """ + # check if point is almost equal to end points + dist = point - self.pt(0), point - self.pt(1) + dist = [item.length() for item in dist] + if min(dist) < ACC: + return True + # check if point is on the lines + [a__, b__, c__] = self.get_abc() + remainder = abs(a__*point.x + b__*point.y + c__) + # print(' coincident: %s %s' % (self.get_name(), point)) + # print(' remainder: %.6f' % remainder) + if remainder < ACC: + # point is on the line equation, but is it between end pts + xvals = [apoint.x for apoint in self.points] + yvals = [apoint.y for apoint in self.points] + xin = min(xvals)-ACC <= point.x <= max(xvals)+ACC + yin = min(yvals)-ACC <= point.y <= max(yvals)+ACC + if xin and yin: + # we're on the line + return True + else: + # we're off of the line + # print(' outside of bounds!') + return False + else: + # point is not on line equation + return False
+ +
[docs] def intersects(self, other): + """Checks if other line intersects this line. + + Args: + other (Line, Arc, Signline, SignArc): other line to check + + Returns: + Point or None: + Point: intersection point + None: None if lines don't intersect + """ + # other is cutline when checking + if isinstance(other, Line) or isinstance(other, SignLine): + terms_1 = self.get_abc() + terms_2 = other.get_abc() + p_1, p_2 = self.pt(0), self.pt(1) + p_3, p_4 = other.pt(0), other.pt(1) + dist = [p_1 - p_3, p_1 - p_4, p_2 - p_3, p_2 - p_4] + vals = [p_1, p_1, p_2, p_2] + dist = [item.length() for item in dist] + # check each of self's point to see if it == other's points + for (distance, val) in zip(dist, vals): + if distance < ACC: + return Point(val.x, val.y) + if terms_1[:2] == terms_2[:2]: + # lines are parallel and will not have an intersection point + #print('Line-Line intersection = None: same slope') + return None + denom = (p_1.x-p_2.x)*(p_3.y-p_4.y) - (p_1.y-p_2.y)*(p_3.x-p_4.x) + if abs(denom) < ACC: + # lines are nearly identical slope + #print('Line-Line intersection = None because of denom') + return None + else: + # intersection calculation + a_array = array([[terms_1[0], terms_1[1]], + [terms_2[0], terms_2[1]]]) + b_array = array([-terms_1[2], -terms_2[2]]) + result = linsolve(a_array, b_array) + point = Point(result[0], result[1]) + # check against existing points on self + dist = [point - p_1, point - p_2] + vals = [p_1, p_2] + dist = [item.length() for item in dist] + for (distance, loc) in zip(dist, vals): + if distance < ACC: + point = Point(loc.x, loc.y) + break + on_self = self.coincident(point) + on_other = other.coincident(point) + if on_self and on_other: + """ + s_name = self.get_name() + o_name = other.get_name() + print('X Checking intersection of %s and %s' % (s_name, o_name)) + print(' %s' % point) + print(' (on_self, on_other): (%s, %s)' % (on_self, on_other)) + """ + return point + else: + """ + print('Line-Line intersection = None point not on lines') + print(' %s' % point) + print(' on_self: %s' % on_self) + print(' on_other: %s' % on_other) + """ + return None + elif isinstance(other, Arc) or isinstance(other, SignArc): + # arc line intersection + return other.intersects(self)
+ + def __str__(self): + """Returns string listing object type, name, and points""" + start = self.pt(0) + end = self.pt(1) + val = 'Line %s, start: %s end: %s' % (self.get_name(), start, end) + return val
+ +
[docs]class SignLine(Line, base_classes.Idobj): + """Makes a signed line. + + Attributes: + line (Line): parent line + sign (int): 1 = positive, or -1 = negative + lineloop (None or LineLoop): parent LineLoop, default is None + faces (list): list of element faces + n1 (list): list of first order nodes on line + nodes (list): nodes on the line + """ + def __init__(self, parent_line, sign): + self.line = parent_line + self.sign = sign + self.lineloop = None + self.faces = [] + self.n1 = [] + base_classes.Idobj.__init__(self) + +
[docs] def pt(self, index): + """Returns the point at the start or end of the line. + + Args: + index (int): 0 = start point, 1 = end point + """ + if self.sign == -1: + return self.line.points[not index] + else: + return self.line.points[index]
+ +
[docs] def set_pt(self, index, point): + """Sets the start or end point of the line to a new point. + + Args: + index (int): 0 for start point, 1 for end point + point (Point): the new point we are updating to + """ + self.line.set_pt(index, point)
+ +
[docs] def set_ediv(self, ediv): + """Applies the element divisions onto the parent line.""" + self.line.set_ediv(ediv)
+ +
[docs] def set_esize(self, esize): + """Applies the element size onto the parent line.""" + self.line.set_esize(esize)
+ +
[docs] def set_lineloop(self, lineloop): + """Sets the parent LineLoop""" + # this is needed to cascade set ediv up to FEA model and down onto + # other lines with this ediv + self.lineloop = lineloop
+ +
[docs] def get_name(self): + """Returns the SignLine name.""" + if self.sign == -1: + return '-L'+str(self.line.id) + else: + return 'L'+str(self.line.id)
+ +
[docs] def label(self, ax): + """Labels the line on the matplotlib ax axis. + + Args: + ax (Matplotlib axis): Matplotlib axis + """ + # find angle of perpendiclar line so we can set text alignment + lvect = self.get_perp_vec() + ang = lvect.ang_deg() + horiz, vert = get_text_hv(ang) + axial = self.midpt.y + radial = self.midpt.x + ax.text(axial, radial, self.get_name(), ha=horiz, va=vert)
+ +
[docs] def plot(self, ax, label=True): + """Draws and labels the line onto the passed Matplotlib axis. + + Args: + ax (Matlotlib axis): Matplotlib axis + label (bool): if True label the line + """ + lax = [pt.y for pt in self.points] + lrad = [pt.x for pt in self.points] + # draw line + ax.plot(lax, lrad) + if label: + self.label(ax)
+ +
[docs] def signed_copy(self, sign): + """Returns a SignLine copy of this line.""" + realsign = self.sign*sign + return SignLine(self.line, realsign)
+ + @property + def nodes(self): + """Gets the line nodes.""" + return self.line.nodes + + @nodes.setter + def nodes(self, nlist): + """Sets the line nodes.""" + self.line.nodes = nlist + + @property + def midpt(self): + """Returns the mid point.""" + return self.line.midpt + + @property + def edge(self): + """Returns a bool saying if this is an edge line.""" + return self.line.edge + + @edge.setter + def edge(self, isedge): + """Sets the line nodes.""" + self.line.edge = isedge + + @property + def points(self): + """Returns a list of the line's points [pstart, pend]""" + pstart = self.pt(0) + pend = self.pt(1) + return [pstart, pend] + +
[docs] def reverse(self): + """Reverses self.line""" + self.line.reverse()
+ + +
[docs]class Arc(base_classes.Idobj): + """Makes an arc from points start_pt to end_pt about center actr. + + Arcs should be 90 degrees maximum. + + Arguments: + start_pt (Point): first point + end_pt (Point): second point + actr (Point): arc center + + Attributes: + points (list of Point) : list of the line's points [start_pt, end_pt] + allpoints (list of Point): [start_pt, end_pt, actr] + actr (Point): arc center + radius (float): radius of the arc + concavity (str): + + - 'concave': concave arc + - 'convex': convex arc + + midpt (Point): a mid-point between p1 and p2 + ediv (None or int): number of elements on arc + nodes (list of Node): a list of meshed nodes on the line + edge (bool): True if arc is a non-shared edge in an area + signlines (list): list of SignArc that use this Arc + """ + + def __init__(self, start_pt, end_pt, actr): + self.points = [start_pt, end_pt] + self.actr = actr + self.actr.arc_center = True + self.radius = (start_pt - actr).length() + self.concavity = self.get_concavity() + self.midpt = self.mid() + self.ediv = None + self.nodes = [] + self.edge = True + self.signlines = [] + base_classes.Idobj.__init__(self) + +
[docs] def reverse(self): + """Reverses self.""" + self.points.reverse() + self.concavity = self.get_concavity()
+ + @property + def allpoints(self): + """Returns all points including arc center""" + return self.points + [self.actr] + + @property + def areas(self): + """Returns all areas that signlines use.""" + areas = set() + for sline in self.signlines: + areas.add(sline.parent) + return list(areas) + + def __eq__(self, other): + """Returns bool telling if self == other + + Returns True if the lines are the same type, and contain the same + list of points. + """ + if isinstance(other, self.__class__): + # check to see if the end points are the same + mypts = self.points + otherpts = other.points + same = True + for (mypt, otherpt) in zip(mypts, otherpts): + if mypt != otherpt: + same = False + break + # flip to false if arc centers are different + myactr = self.actr + otheractr = other.actr + if myactr != otheractr: + same = False + return same + else: + return False + + def __hash__(self): + """Set the hash of the item to the id number. + + This allows one to make sets of these items. + """ + return self.id + +
[docs] def add_signline(self, signline): + """Adds a signline to self.signlines.""" + if signline not in self.signlines: + self.signlines.append(signline)
+ +
[docs] def get_name(self): + """Returns the arc name.""" + return 'L'+str(self.id)
+ +
[docs] def length(self): + """Returns the length of the arc.""" + a = self.pt(0)-self.actr + b = self.pt(1)-self.actr + ang = abs(a.ang_bet_rad(b)) + res = self.radius*ang + return res
+ +
[docs] def save_to_points(self): + """This stores this line in the line's child points.""" + for point in self.allpoints: + point.set_line(self)
+ +
[docs] def set_ediv(self, ediv): + """Sets the number of element divisions on the arc when meshing. + + Args: + ediv (int): number of required elements on this arc + """ + self.ediv = ediv
+ +
[docs] def set_esize(self, esize): + """Sets the size of mesh elements on the arc when meshing. + + Args: + esize (float): size of mesh elements on this arc + """ + + for i in range(2): + self.pt(i).set_esize(esize)
+ +
[docs] def signed_copy(self, sign): + """Returns a SignArc instance of this Arc with the passed sign.""" + return SignArc(self, sign)
+ +
[docs] def pt(self, ind): + """Returns the start or end point of the arc. + + Args: + ind (int): index of the point we want, 0=start, 1=end + + Returns: + Point: requested Point + """ + return self.points[ind]
+ +
[docs] def set_pt(self, ind, point): + """Update the arc's point at ind to the new point. + + Args: + ind (int): index of the point we're updating, 0=start, 1=end + point (Point): new point we are assigning to Line + """ + # remove this line from the old point + self.points[ind].lines.remove(self) + # set the new point + self.points[ind] = point + self.midpt = self.mid() + self.save_to_points() + other_point = self.pt(int(not ind)) + if point == other_point: + other_point.lines.remove(self) + for sline in self.signlines: + sline.lineloop.remove(sline) + if self.id != -1: + if len(self.signlines) > 0: + area = self.signlines[0].lineloop.parent + area.part.fea.lines.remove(self)
+ +
[docs] def mid(self): + """Caclulates and returns the midpoint of the arc.""" + pt = self.get_pt_at(0.5) + return pt
+ +
[docs] def touches(self, other): + """Checks if this arc is connected to a passed line. + + Args: + other (Line or Arc): passed other line + + Returns: + True: beginning or end point connect to other line + False: lines are not connected + """ + [p1, p2] = [self.pt(0), self.pt(1)] + if p1 in other.points or p2 in other.points: + return True + else: + return False
+ +
[docs] @staticmethod + def sgn(val): + """Returns sign of the passed value. + + Helper function for self.interects code. + + Args: + val (float): passed value + + Returns: + 1 or -1: sign of the passed value, assumes sign of zero = 1 + """ + if val < 0: + return -1 + else: + return 1
+ +
[docs] def get_ang(self): + """ Returns angle from beginning to end of arc from arc ctr in degrees + + Notes: + Answer is between 180 and -180 + Positive is concave + Negative is convex + """ + a = self.pt(0)-self.actr + b = self.pt(1)-self.actr + ang = a.ang_bet_deg(b) + return ang
+ +
[docs] def get_ang_rad(self): + """ Returns angle from beginning to end of arc from arc ctr in rad + + Notes: + Answer is between pi and -pi + Positive is concave + Negative is convex + """ + a = self.pt(0)-self.actr + b = self.pt(1)-self.actr + ang = a.ang_bet_rad(b) + return ang
+ +
[docs] def get_concavity(self, clockwise=True): + """Returns concavity of this arc, 'concave' or 'convex'.""" + ang = self.get_ang() + res = '' + if clockwise: + res = 'concave' + if ang < 0: + res = 'convex' + else: + res = 'convex' + if ang < 0: + res = 'concave' + return res
+ +
[docs] def get_pt_at(self, nondim): + """Returns point on arc between start and end point at nondim dist. + + Args: + nondim (float): distance between start and end point, 0=start, 1=end + + Returns: + res (Point): requested point on arc + """ + a = self.pt(0)-self.actr + + # neg is convex, pos is concave + ang = self.get_ang() + + ang_delta = nondim*ang + a.rot_ccw_deg(ang_delta) + + res = self.actr + a + return res
+ +
[docs] def get_perp_vec(self, pt): + """ Returns vector perpendicular to current arc. + + Right hand rule is used: + X+ is the direction of the arc from start to end. + Z+ is out of the page + Y+ is the direction of the perpendicular vector + + Args: + pt (Point): point on arc where we want the perpendicular vector + + Returns: + resv (Point): perpendicular vector + """ + resv = None + if self.concavity == 'convex': + resv = pt - self.actr + elif self.concavity == 'concave': + resv = self.actr - pt + return resv
+ +
[docs] def get_tan_vec(self, point): + """ Returns vector tangent to the current arc. + + Vector points from the passed point to a unit location further away. + + Args: + point (Point): start point + + Returns: + resv (Point): vector that is tangent to the current line + """ + if self.concavity == 'concave': + resv = self.actr - point + elif self.concavity == 'convex': + resv = point - self.actr + if point == self.pt(0): + resv.rot_ccw_deg(-90) + else: + resv.rot_ccw_deg(90) + resv.make_unit() + return resv
+ +
[docs] def get_verts_codes(self, plot=True): + """Returns a list of [verts, codes] vertices for plotting.""" + # http://www.spaceroots.org/documents/ellipse/node19.html + # http://www.tinaja.com/glib/bezarc1.pdf + # http://stackoverflow.com/questions/734076/geometrical-arc-to-bezier-curve + + # find the angle of rotation to the middle of the arc + ang_offset = self.get_pt_at(0.5) - self.actr + ang_offset = ang_offset.ang_deg() + + # store the half angle as theta + theta = radians(abs(self.get_ang()))*0.5 + + ax1 = (4 - cos(theta))/3 + rad1 = ((1 - cos(theta))*(cos(theta) - 3))/(3*sin(theta)) + pt1 = Point(rad1, ax1)*self.radius + pt2 = Point(-rad1, ax1)*self.radius + + # fix the angles + pt1.rot_ccw_deg(ang_offset) + pt2.rot_ccw_deg(ang_offset) + + # fix the location + pt1 += self.actr + pt2 += self.actr + + # need to change the order if the arc is concave or convex + verts = [pt1, pt2, self.pt(1)] + if self.concavity == 'convex': + verts = [pt2, pt1, self.pt(1)] + + if plot: + verts = [point.axrad() for point in verts] + else: + verts = [point.radax() for point in verts] + codes = [Path.CURVE4, Path.CURVE4, Path.CURVE4] + return [verts, codes]
+ +
[docs] def coincident(self, pt): + """Checks to see if pt is on the arc. + + Args: + pt (Point): input point to check + + Returns: + bool: True if pt on arc or False if not + """ + # (x - xc)^2 + (y - yc)^2 - r^2 = 0 + dist = pt - self.pt(0), pt - self.pt(1) + dist = [item.length() for item in dist] + if min(dist) < ACC: + # True if point almost equal to end points + return True + remainder = abs((pt.x - self.actr.x)**2 + (pt.y - self.actr.y)**2 + - self.radius**2) + if remainder < ACC: + # point is on the circle, but is it in the arc? + # check if the angle traversed to the point is within the ang + # traversed by the arc + a1 = self.pt(0)-self.actr + a2 = pt-self.actr + ang_pt = a1.ang_bet_deg(a2) + + # this is the angle traversed byt the arc + ang = self.get_ang() + minval = min(0, ang) + maxval = max(0, ang) + if minval <= ang_pt <= maxval: + # point is on our arc + return True + else: + # point on circle but outside our arc + return False + else: + # point not on arc + return False
+ +
[docs] def intersects(self, other): + """Checks if other line intersects this arc. + + Args: + other (Line or Arc): other line to check + + Returns: + Point or None: + Point: intersection point + None: None if lines don't intersect + """ + if isinstance(other, Line) or isinstance(other, SignLine): + #print('|C Checking arc-line intersection') + #print(' %s' % self) + # arc-line intersection + # reset the math to be about the circle centroid + # http://mathworld.wolfram.com/Circle-LineIntersection.html + pt1 = other.pt(0) - self.actr + pt2 = other.pt(1) - self.actr + # for us x and y are reversed because of y axial + (x1, y1, x2, y2) = (pt1.y, pt1.x, pt2.y, pt2.x) + dx = x2 - x1 + dy = y2 - y1 + rad = self.radius + dr = (dx**2 + dy**2)**(0.5) + D = x1*y2 - x2*y1 + dist = [self.pt(0) - other.pt(0), self.pt(0) - other.pt(1), + self.pt(1) - other.pt(0), self.pt(1) - other.pt(1)] + vals = [self.pt(0), self.pt(0), self.pt(1), self.pt(1)] + dist = [item.length() for item in dist] + for (distance, val) in zip(dist, vals): + if distance < ACC: + return Point(val.x, val.y) + discr = 1.0*(rad**2)*(dr**2) - D**2 + if discr < 0: + # no intersection + #print('none returned because of discriminant') + return None + else: + # tangent or intersection + rsq = rad**2 + drsq = dr**2 + x1 = (D*dy + self.sgn(dy)*dx*(rsq*drsq-D**2)**(0.5))/drsq + y1 = (-D*dx + abs(dy)*(rsq*(dr**2)-D**2)**(0.5))/drsq + x2 = (D*dy - self.sgn(dy)*dx*(rsq*drsq-D**2)**(0.5))/drsq + y2 = (-D*dx - abs(dy)*(rsq*drsq-D**2)**(0.5))/drsq + + # convert result points back to global coordinates + pt1 = Point(y1, x1) + self.actr + pt2 = Point(y2, x2) + self.actr + res = [] + # check that the resultant points are on line and arcs + if self.coincident(pt1) and other.coincident(pt1): + for apoint in self.points: + dist = pt1 - apoint + dist = dist.length() + if dist < ACC: + pt1 = Point(apoint.x, apoint.y) + break + res.append(pt1) + if pt1 != pt2 and self.coincident(pt2) and other.coincident(pt2): + for apoint in self.points: + dist = pt2 - apoint + dist = dist.length() + if dist < ACC: + pt2 = Point(apoint.x, apoint.y) + break + res.append(pt2) + if len(res) == 1: + return res[0] + elif len(res) > 1: + return res + elif len(res) == 0: + #print('none returned because no point from math') + return None + elif isinstance(other, Arc): + # arc-arc intersection + # I have not yet written the code here + return None
+ + def __str__(self): + """Returns string listing object type, name, and points""" + name = self.get_name() + start, end = self.pt(0), self.pt(1) + actr = self.actr + val = 'Arc %s, start: %s end: %s center: %s' % (name, start, end, actr) + return val
+ +
[docs]class SignArc(Arc, base_classes.Idobj): + """Makes a signed arc. + + Attributes: + line (Arc): parent arc + sign (int): 1 = positive, or -1 = negative + lineloop (None or LineLoop): parent LineLoop, default is None + nodes (list): nodes on the arc + faces (list): list of element faces + n1 (list): list of first order nodes on line + """ + + def __init__(self, parent_line, sign): + self.line = parent_line + self.sign = sign + self.lineloop = None + self.faces = [] + self.n1 = [] + base_classes.Idobj.__init__(self) + +
[docs] def pt(self, ind): + """Returns the point at the start or end of the line.""" + if self.sign == -1: + return self.line.points[not ind] + else: + return self.line.points[ind]
+ +
[docs] def set_pt(self, ind, point): + """Sets the passed point to at the index location.""" + self.line.set_pt(ind, point)
+ +
[docs] def set_ediv(self, ediv): + """Apply the element divisions onto the parent line.""" + self.line.set_ediv(ediv)
+ +
[docs] def set_esize(self, esize): + """Applies the element size onto the parent line.""" + self.line.set_esize(esize)
+ +
[docs] def set_lineloop(self, lineloop): + """Sets the parent LineLoop""" + # this is needed to cascade set ediv up to FEA model and down onto + # other lines with this ediv + self.lineloop = lineloop
+ +
[docs] def get_name(self): + """Returns the line name.""" + if self.sign == -1: + return '-L'+str(self.line.id) + else: + return 'L'+str(self.line.id)
+ +
[docs] def label(self, ax): + """Labels the arc on the matplotlib ax axis. + + Args: + ax (Matplotlib axis): Matplotlib axis + """ + lvect = self.get_perp_vec(self.midpt) + ang = lvect.ang_deg() + horiz, vert = get_text_hv(ang) + (axial, radial) = (self.midpt.y, self.midpt.x) + ax.text(axial, radial, self.get_name(), ha=horiz, va=vert)
+ +
[docs] def plot(self, ax, label=True): + """Draws and labels the arc onto the passed Matplotlib axis. + + Args: + ax: Matplotlib axis + label (bool): if True, label the arc + """ + ctr = self.actr + vect1 = None + sign = 1 + if self.concavity == 'concave': + vect1 = self.pt(0)-ctr + else: + sign = -1 + vect1 = self.pt(1)-ctr + rad = self.radius + ang1 = (vect1.ang_deg()) + ang2 = ang1 + sign*self.get_ang() + + # matplotlib assumes ccw arc drawing, calculix assumes cw drawing + a = AArc(xy=[ctr.y, ctr.x], width=2*rad, height=2*rad, angle=0, + theta1=ang1, theta2=ang2) + ax.add_artist(a) + if label: + self.label(ax)
+ +
[docs] def signed_copy(self, sign): + """Returns a SignArc copy of this arc.""" + realsign = self.sign*sign + return SignArc(self.line, realsign)
+ + @property + def nodes(self): + """Gets the line nodes.""" + return self.line.nodes + + @nodes.setter + def nodes(self, nlist): + """Sets the line nodes.""" + self.line.nodes = nlist + + @property + def actr(self): + """Return the arc center.""" + return self.line.actr + + @property + def radius(self): + """Return the arc radius.""" + return self.line.radius + + @property + def midpt(self): + """Return the mid point.""" + return self.line.midpt + + @property + def edge(self): + """Returns a bool saying if this is an edge line.""" + return self.line.edge + + @edge.setter + def edge(self, isedge): + """Sets the line nodes.""" + self.line.edge = isedge + + @property + def points(self): + """Return a list of the arc's points [pstart, pend]""" + pstart = self.pt(0) + pend = self.pt(1) + return [pstart, pend] + + @property + def concavity(self): + """Return the arc's concavity 'concave' or 'convex'""" + if self.sign == 1: + return self.line.concavity + else: + if self.line.concavity == 'concave': + return 'convex' + else: + return 'concave' + +
[docs] def reverse(self): + """Reverses self.line""" + self.line.reverse()
+ + +
[docs]class LineLoop(list): + """Makes a LineLoop, which stores multiple Line/Arc or SignLine/SignArc. + + Args: + items (list): Line or Arc or SignLine or SignArc + hole_bool (bool): True if hole, False otherwise + parent (Area or None): parent Area, defaults to None + + Attributes: + closed (bool): True if closed + area (float): the loop's area, can be pos or neg. Pos = cw, neg = ccw + hole (bool): True if loop is a hole + ccw (bool): True if loop is counter-clockwise + center (Point): a point at the loop centroid + parent (Area or None): the loop's parent Area + """ + + def __init__(self, items=[], hole_bool=False, parent=None): + super().__init__(items) + self.id = -1 + for item in self: + if isinstance(item, SignLine) or isinstance(item, SignArc): + item.lineloop = self + if item not in item.line.signlines: + item.line.add_signline(item) + self.hole = hole_bool + self.parent = parent + +
[docs] def set_id(self, id): + """Sets the id number""" + self.id = id
+ + def __hash__(self): + """Sets the hash of the item to the id number. + + This allows one to make sets of these items. + """ + return self.id + + @property + def closed(self): + """Returns True if Closed, False if not.""" + if len(self) < 2: + return False + else: + for ind, curr_line in enumerate(self): + lower_ind = ind-1 + prev_line = self[lower_ind] + if curr_line.pt(0) != prev_line.pt(1): + return False + return True + + @property + def center(self): + """Returns None or a Point at the line loop's center.""" + if self.closed == False: + return None + else: + cx_sum = 0 + cy_sum = 0 + a_sum = 0 + arcs = [] + for item in self: + if isinstance(item, Line) or isinstance(item, SignLine): + start, end = item.pt(0), item.pt(1) + det_val = det([[start.x, end.x], [start.y, end.y]]) + cx_sum += (start.x + end.x)*det_val + cy_sum += (start.y + end.y)*det_val + a_sum += det_val + elif isinstance(item, Arc) or isinstance(item, SignArc): + # concave: draw to actr, adder is neg + # convex: draw to actr, adder is pos + tmp_list = [[item.pt(0), item.actr], + [item.actr, item.pt(1)]] + for [start, end] in tmp_list: + det_val = det([[start.x, end.x], [start.y, end.y]]) + cx_sum += (start.x + end.x)*det_val + cy_sum += (start.y + end.y)*det_val + a_sum += det_val + arcs.append(item) + area_val = a_sum*0.5 + val_list = [] + if area_val != 0: + cx_val = (1/(6*area_val))*cx_sum + cy_val = (1/(6*area_val))*cy_sum + val_list.append([area_val, cx_val, cy_val]) + for arc in arcs: + ang = arc.get_ang_rad() + area_val = -0.5*ang*(arc.radius**2) + half_ang = abs(ang)*0.5 + vect = arc.midpt - arc.actr + vect.make_unit() + mag = 2*arc.radius*sin(half_ang)/(3*half_ang) + vect = vect*mag + center = arc.actr + vect + val_list.append([area_val, center.x, center.y]) + a_sum = sum([aval for [aval, cx, cy] in val_list]) + cxa_sum = sum([cx*aval for [aval, cx, cy] in val_list]) + cya_sum = sum([cy*aval for [aval, cx, cy] in val_list]) + cx_val = cxa_sum/a_sum + cy_val = cya_sum/a_sum + return Point(cx_val, cy_val) + + @property + def ccw(self): + """Returns bool telling if the area is closed and clockwise.""" + if self.closed == False: + return False + else: + area_val = self.area + if area_val < 0: + return True + else: + return False + + @property + def area(self): + """Returns the loop's signed area. + + Returns 0 if not closed. + Pos = cw, neg = ccw + + Formula from: http://mathworld.wolfram.com/PolygonArea.html + """ + res = 0 + adder = 0 + arcs = [] + if self.closed == False: + return res + else: + for item in self: + if isinstance(item, Line) or isinstance(item, SignLine): + start, end = item.pt(0), item.pt(1) + det_matrix = [[start.x, end.x], [start.y, end.y]] + res += det(det_matrix) + elif isinstance(item, Arc) or isinstance(item, SignArc): + # concave: draw to actr, adder is neg + # convex: draw to actr, adder is pos + start, end = item.pt(0), item.actr + det_matrix = [[start.x, end.x], [start.y, end.y]] + res += det(det_matrix) + start, end = item.actr, item.pt(1) + det_matrix = [[start.x, end.x], [start.y, end.y]] + res += det(det_matrix) + arcs.append(item) + res = 0.5*res + # need to loop through arcs here, now that we know if cw or ccw + for arc in arcs: + ang = arc.get_ang_rad() + tmp_adder = -0.5*ang*(arc.radius**2) + adder += tmp_adder + res = res + adder + return res + +
[docs] def reverse(self): + """Reverse the loop direction cw<-->ccw + + This modifies the order of lines, and flips each line. + """ + for line in self: + line.reverse() + super().reverse()
+ +
[docs] def set_parent(self, parent): + """Sets the line loop's parent part.""" + self.parent = parent
+ +
[docs] def get_patch(self): + """Returns a patch of the LineLoop, or None of not closed.""" + if self.closed: + codes, verts = [], [] + # start poly + codes += [Path.MOVETO] + verts += [self[0].pt(0).radax()] + for line in self: + if isinstance(line, SignLine) or isinstance(line, Line): + verts.append(line.pt(1).radax()) + codes.append(Path.LINETO) + elif isinstance(line, SignArc) or isinstance(line, Arc): + lverts, lcodes = line.get_verts_codes(plot=False) + verts += lverts + codes += lcodes + if line == self[-1]: + # close poly if at last line + verts.append((0, 0)) + codes.append(Path.CLOSEPOLY) + path = Path(verts, codes) + patch = PathPatch(path, facecolor='yellow', linewidth=0.0) + return patch + return None
+ +
[docs] def contains_point(self, point): + """Returns bool telling if pt is inside the LineLoop. + + Args: + pt (Point): point we are checking + + Returns: + contains (bool): True if in this area False if not + """ + contains = False + if self.closed: + patch = self.get_patch() + contains = patch.contains_point(point.radax()) + return contains
+ +
[docs] def inside(self, other): + """Checks to see if self is inside other. + + Only checks self's points. + """ + if self.closed == False: + return False + if isinstance(other, LineLoop): + points = set() + for sline in self: + points.update(sline.points) + patch = other.get_patch() + for point in points: + contains = patch.contains_point(point.radax()) + if contains == False: + return False + return True + else: + return False
+ +
[docs] def append(self, item): + """Adds an item to this LineLoop + + Args: + item (SingArc or SignLine): item to add to the list + """ + if isinstance(item, SignLine) or isinstance(item, SignArc): + item.set_lineloop(self) + super().append(item) + return item
+ +
[docs] def remove(self, item): + """Removes an SignLine or SignArc item from this LineLoop + + Args: + item (SignArc or SignLine): item to remove + """ + if isinstance(item, SignLine) or isinstance(item, SignArc): + item.set_lineloop(None) + if item not in item.line.signlines: + print('''===THIS SLINE NOT IN SLINE.LINE.SIGNLINES!===''') + print('Removing sline %s from loop in area %s' + % (item.get_name(), self.parent.get_name())) + print('SLINE.LINE %s' % item.line) + print('SLINE.LINE.SIGNLINES %r' % item.line.signlines) + super().remove(item)
+ +
[docs] def insert(self, index, item): + """Inserts a SignLine or SignArc item into this LineLoop + + Args: + item (SingArc or SignLine): item to insert + """ + if isinstance(item, SignLine) or isinstance(item, SignArc): + item.set_lineloop(self) + super().insert(index, item)
+ + def __str__(self): + """Prints the LineLoop definition.""" + res = 'Number of items %i\n' % len(self) + for line in self: + res += '%s\n' % line + return res
+ +
[docs]class Area(base_classes.Idobj): + """ Makes an area. + + Area is closed in a clockwise direction. + + Args: + part (Part): parent Part + line_list (list): list of Lines and Arcs that close the area + + Attributes: + part (Part): parent part + closed (boolean): True if closed, False if not + exlines (LineLoop): LineLoop of signlines that define the area exterior + signlines (list): list of signed lines or arcs that define the area + lines (list): a list of all the lines that make the area, includes hole + lines + points (list): a list of all points making the area, excludes arc centers + allpoints (list): a list of all points, includes arc centers + holepoints (list): a list of hole points, excludes arc centers + matl (Matl): the material fo the area + etype (str): element type of area. Options are: + 'plstress': plane stress + 'plstrain': plane strain + 'axisym': axisymmetric + area (double): in-plane part area (ballpark number, arcs not calc right) + holes (list): list of LineLoop of signlines + center (Point or None): the area centroid point. None before closed. + nodes (list): child mesh nodes that are in the area + elements (list): child elements that are in the area + """ + + def __init__(self, part, line_list=[]): + base_classes.Idobj.__init__(self) + self.part = part # parent part reference + self.closed = False + self.matl = None + self.etype = None + self.area = 0.0 + self.holes = [] + self.center = None + self.nodes = [] + self.elements = [] + exloop = LineLoop(line_list, False, self) + self.part.fea.register(exloop) + self.exlines = exloop + if self.exlines.closed == True: + self.close() + + def __hash__(self): + """Returns the item's id as its hash.""" + return self.id + + @property + def lines(self): + """Returns a list of all Line and Arc under this area.""" + mylist = [] + loops = [self.exlines] + self.holes + for loop in loops: + line_list = [line.line for line in loop] + mylist += line_list + return mylist + + @property + def signlines(self): + """Returns a list of all SignLine and SignArc under this area.""" + mylist = [] + loops = [self.exlines] + self.holes + for loop in loops: + mylist += loop + return mylist + + @property + def points(self): + """Returns a list of all points under this area; excludes arc centers.""" + pts = set() + for line in self.lines: + pts.update(line.points) + return list(pts) + + @property + def allpoints(self): + """Returns a list of all points under this area; includes arc centers.""" + pts = set() + for line in self.lines: + pts.update(line.allpoints) + return list(pts) + + @property + def holepoints(self): + """Returns a list of all points in this area's holes.""" + pts = set() + for hole in self.holes: + for sline in hole: + pts.update(sline.points) + return list(pts) + + def __str__(self): + """Returns a string listing the area's name.""" + return self.get_name() + +
[docs] def get_name(self): + """Returns the area name.""" + return 'A'+str(self.id)
+ +
[docs] def close(self): + """Closes the area.""" + # sets closed=True + # calculates the area center + self.closed = True + if self.exlines.ccw == True: + self.exlines.reverse() + self.area, self.center = self.calc_area_center()
+ +
[docs] def line_from_startpt(self, pt): + """Returns the signline in the area that start at the passed pt. + + Args: + pt (Point): the start point of the line one wants + + Returns: + match (None or SignLine or SignArc): + | Returns None if no line is found, otherwise the correct line or + | arc which starts with pt will be + | returned. + """ + # returns the line that starts on the given point + matches = [] + area_slines = self.signlines + for sline in area_slines: + if sline.pt(0) == pt: + matches.append(sline) + if len(matches) == 1: + return matches[0] + elif len(matches) > 1: + # we have a saw cut in the area and we're cutting on one of its pts + print('>1 slines starting with %s' % pt.get_name()) + for match in matches: + slines = set(match.line.signlines) + if len(slines) == 2 and slines.issubset(area_slines): + # this is the saw cut one + pass + else: + # we want the non saw cut one + return match + return None
+ +
[docs] def calc_area_center(self): + """Calculates and returns the area and centroid Point. + + Returns: + list: [area, Point] + """ + loops = [self.exlines] + self.holes + val_list = [] + for loop in loops: + if loop.closed == True: + aval, cval = loop.area, loop.center + val_list.append([aval, cval]) + a_sum = sum([aval[0] for aval in val_list]) + cxa_sum = sum([center.x*aval for [aval, center] in val_list]) + cya_sum = sum([center.y*aval for [aval, center] in val_list]) + cx_val = cxa_sum/a_sum + cy_val = cya_sum/a_sum + center = Point(cx_val, cy_val) + return [a_sum, center]
+ +
[docs] def get_maxlength(self): + """Returns max distance between points in an area.""" + pts = self.points + maxlen = 0.0 + # loop through points checking dist to next point + for ind, p1 in enumerate(pts[:-1]): + for p2 in pts[ind:]: + vect = p1 - p2 + dist = vect.length() + if dist > maxlen: + maxlen = dist + return maxlen
+ +
[docs] def rem_sline(self, sline): + """Removes a sline from this area.""" + loops = [self.exlines] + self.holes + for loop in loops: + if sline in loop: + print('Line %s removed from %s' + % (sline.get_name(), self.get_name())) + loop.remove(sline) + if sline.id != -1: + line = sline.line + self.part.fea.signlines.remove(sline) + if len(line.signlines) == 0: + self.part.fea.lines.remove(line)
+ +
[docs] def add_sline(self, signline): + """Adds signline to the area definition. + + SignLine is added to the end of the lines list. + Area is closed if needed. + """ + # this adds a line to the area + self.exlines.append(signline) + if self.exlines.closed: + self.close()
+ +
[docs] def add_hole_sline(self, signline): + """Adds signline to the area hole definition. + + Line is added to the end of the lines list. + Area is closed if needed. + + Returns: + closed (bool): boolean telling if the hole was closed + """ + make_new = (len(self.holes) == 0) + if make_new == False: + if self.holes[-1].closed == True: + make_new = True + if make_new: + hole = LineLoop([], hole_bool=True, parent=self) + hole = self.part.fea.lineloops.append(hole) + self.holes.append(hole) + self.holes[-1].append(signline) + # reverse the hole if it's closed in the cw direction + if self.holes[-1].closed == True: + if self.holes[-1].ccw == False: + self.holes[-1].reverse() + self.area, self.center = self.calc_area_center() + return self.holes[-1].closed
+ +
[docs] def get_patch(self): + """Returns a patch of the area.""" + if self.closed: + loops = [self.exlines] + self.holes + codes, verts = [], [] + for loop in loops: + # start poly + codes += [Path.MOVETO] + verts += [loop[0].pt(0).axrad()] + for sline in loop: + if isinstance(sline, SignLine): + verts.append(sline.pt(1).axrad()) + codes.append(Path.LINETO) + elif isinstance(sline, SignArc): + lverts, lcodes = sline.get_verts_codes() + verts += lverts + codes += lcodes + if sline == loop[-1]: + # close poly if at last line + verts.append((0, 0)) + codes.append(Path.CLOSEPOLY) + path = Path(verts, codes, _interpolation_steps=2) + patch = PathPatch(path, linewidth=0.0) + return patch
+ +
[docs] def label(self, axis): + """Labels the area on a Matplotlib axis + + Args: + axis (Matplotlib Axis): Matplotlib Axis + """ + axis.text(self.center.y, self.center.x, self.get_name(), + ha='center', va='center')
+ +
[docs] def plot(self, axis, label=True, color='yellow'): + """Plots the area on a Matplotlib axis. + + Args: + axis (Matplotlib axis): Matplotlib axis + label (bool): if True, label area + """ + # http://stackoverflow.com/questions/8919719/how-to-plot-a-complex-polygon + if self.closed: + # plot area patch + patch = self.get_patch() + patch.set_color(color) + axis.add_patch(patch) + # label ares + if label: + self.label(axis)
+ +
[docs] def contains_point(self, point): + """Returns bool telling if pt is inside the area. + + Args: + pt (Point): point we are checking + + Returns: + contains (bool): True if in this area False if not + """ + patch = self.get_patch() + contains = patch.contains_point(point.axrad()) + return contains
+ +
[docs] def line_insert(self, lgiven, lnew, after=True): + """Inserts line lnew before or after the given line. + + Args: + lgiven (SignLine or SignArc): the line we are inserting relative to + lnew (SignLine or SignArc): the new line we will insert + after (bool): if True insert lnew after lgiven, false insert before + + Returns: + bool: True if succeeded, False if failed + """ + if lgiven in self.signlines: + print('Inserting line %s into area %s next to %s' % + (lnew.get_name(), self.get_name(), lgiven.get_name())) + lineloop = lgiven.lineloop + type = 'exterior lines' + if lineloop.hole: + type = 'hole lines' + ind = lineloop.index(lgiven) + if after: + ind += 1 + print(' Inserting line into %s outer lines' % self.get_name()) + lineloop.insert(ind, lnew) + return True + else: + print('ERROR: Passed line was not in %s!' % self.get_name()) + print('You must pass a line in this area!') + return False
+ +
[docs] def set_child_ccxtypes(self): + """Assigns the child element ccx types.""" + if self.etype != None and self.elements != []: + # if etype is set and elements have been assigned + for element in self.elements: + element.set_ccxtype(self.etype)
+ +
[docs] def set_etype(self, etype): + """Sets the area etype. + + Sets the ccxtype on elements if they have been assigned to this area. + + Args: + etype (str): element type + 'plstress' plane stress + 'plstrain': plane strain + 'axisym': axisymmetric + """ + self.etype = etype + self.set_child_ccxtypes()
+ +
[docs] def set_esize(self, esize): + """Sets the size of mesh elements on the area when meshing. + + Args: + esize (float): size of mesh elements on this area + """ + #size if set if no mesh size on points is peresent + for p in self.points: + p.set_esize(esize)
+ +
[docs] def update(self, line_list): + """Updates the area's external lines to the passed list. + + If the area is cosed, self.close() will be called. + + Args: + line_list (list): list of SignLine and SignArc items + """ + to_rem = list(self.exlines) + for sline in to_rem: + self.exlines.remove(sline) + for sline in line_list: + self.exlines.append(sline) + if self.exlines.closed == True: + self.close()
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/installer.html b/dist/documentation/_modules/pycalculix/installer.html new file mode 100644 index 0000000..3b2780d --- /dev/null +++ b/dist/documentation/_modules/pycalculix/installer.html @@ -0,0 +1,534 @@ + + + + + + + pycalculix.installer — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.installer

+
+import glob
+import os
+import re
+import shutil
+import shlex
+import subprocess
+import sys
+from urllib.parse import urlparse
+from urllib.parse import ParseResult
+import zipfile
+
+from sys import platform as platform
+
+import requests
+
+WINDOWS_PLATFORMS = ['win32', 'win64']
+LINUX_PLATFORMS = ['linux', 'linux2']
+
+
[docs]def printos(os, bits): + """Prints the operating system and bit size""" + print('Detected {} {} bit'.format(os, bits))
+ +
[docs]def add(): + """Installs the fea tools on windows/mac/linux""" + osname = None + is_64bit = sys.maxsize > 2**32 + bitsize_dict = {True: 64, False: 32} + bitsize = bitsize_dict[is_64bit] + if platform in LINUX_PLATFORMS: + printos('Linux', bitsize) + ubuntu_add() + elif platform == "darwin": + printos('Mac OS X', bitsize) + mac_add() + elif platform in WINDOWS_PLATFORMS: + printos('Windows', bitsize) + windows_add(bitsize) + print('Done!')
+ +
[docs]def remove(): + """Removes the fea tools on windows/mac/linux""" + osname = None + is_64bit = sys.maxsize > 2**32 + bitsize_dict = {True: 64, False: 32} + bitsize = bitsize_dict[is_64bit] + if platform in LINUX_PLATFORMS: + printos('Linux', bitsize) + ubuntu_remove() + elif platform == "darwin": + printos('Mac OS X', bitsize) + mac_remove() + elif platform in WINDOWS_PLATFORMS: + printos('Windows', bitsize) + windows_remove(bitsize) + print('Done!')
+ +
[docs]def ubuntu_add(): + """Adds ccx and gmsh programs on ubuntu, uses apt""" + gmsh_installed = shutil.which('gmsh') + if not gmsh_installed: + print('Installing gmsh') + command_line = "sudo apt-get install gmsh" + subprocess.check_call(command_line, shell=True) + else: + print('gmsh present') + ccx_installed = shutil.which('ccx') + if not ccx_installed: + print('Installing calculix (ccx)') + command_line = "sudo apt-get install calculix-ccx" + subprocess.check_call(command_line, shell=True) + else: + print('calculix (ccx) present')
+ +
[docs]def ubuntu_remove(): + """Removes ccx and gmsh programs on ubuntu, uses apt""" + ccx_installed = shutil.which('ccx') + if not ccx_installed: + print('calculix (ccx) is not on your system') + else: + print('Removing calculix (ccx)') + command_line = "sudo apt-get remove calculix-ccx" + subprocess.check_call(command_line, shell=True) + gmsh_installed = shutil.which('gmsh') + if not gmsh_installed: + print('gmsh is not on your system') + else: + print('Removing gmsh') + command_line = "sudo apt-get remove gmsh" + subprocess.check_call(command_line, shell=True)
+ +
[docs]def mac_add(): + """Adds ccx and gmsh programs on mac, uses brew""" + brew_installed = shutil.which('brew') + if not brew_installed: + print('Installing brew') + url = 'https://raw.githubusercontent.com/Homebrew/install/master/install' + command_line = "/usr/bin/ruby -e \"$(curl -fsSL %s)\"" % url + subprocess.check_call(command_line, shell=True) + else: + print('brew present') + gmsh_installed = shutil.which('gmsh') + if not gmsh_installed: + print('Installing gmsh') + folder_path = os.path.dirname(os.path.abspath(__file__)) + dmginstall_path = os.path.join(folder_path, 'dmginstall.sh') + url = 'http://gmsh.info/bin/MacOSX/gmsh-3.0.5-MacOSX.dmg' + command_line = '%s %s' % (dmginstall_path, url) + print('command_line=%s' % command_line) + subprocess.check_call(command_line, shell=True) + gmsh_path = '/Applications/Gmsh.app/Contents/MacOS/gmsh' + command_line = "ln -s %s /usr/local/bin/gmsh" % gmsh_path + subprocess.check_call(command_line, shell=True) + else: + print('gmsh present') + ccx_installed = shutil.which('ccx') + if not ccx_installed: + mac_add_ccx() + else: + print('calculix (ccx) present')
+ +
[docs]def mac_add_ccx(): + print('Installing calculix (ccx)') + command_line = "brew install brewsci/science/calculix-ccx" + subprocess.check_call(command_line, shell=True) + ccx_path = find_brew_binary_location('calculix-ccx', 'ccx') + if not ccx_path: + raise Exception('Failed to find ccx binary') + # link the ccx command to the ccx binary + command_line = "ln -s %s /usr/local/bin/ccx" % ccx_path + subprocess.check_call(command_line, shell=True) + # check to see if we have the fortran that ccx needs and return if we do + gcc7_to_path = "/usr/local/opt/gcc/lib/gcc/7" + + needed_fortran_path = "%s/libgfortran.4.dylib" % gcc7_to_path + if os.path.isfile(needed_fortran_path): + if os.path.islink(link_to_path): + print('Linked gcc7 fortran found and used') + else: + print('System gcc7 fortran found and used') + print('Finished installing calculix (ccx)') + return + # install gcc@7 with brew if we don't have it + paths = glob.glob("/usr/local/Cellar/gcc@7/*") + if not paths: + command_line = "brew install gcc@7" + subprocess.check_call(command_line, shell=True) + print('Installed gcc@7 (needed by calculix)') + paths = glob.glob("/usr/local/Cellar/gcc@7/*") + # link gcc@7 from_path to to_path + gcc7_from_path = "%s/lib/gcc/7" % paths[-1] + command_line = "ln -s %s %s" % (gcc7_from_path, gcc7_to_path) + subprocess.check_call(command_line, shell=True) + if not os.path.isfile(needed_fortran_path): + raise Exception("Install failed, libgfortran.4.dylib (a library that " + "ccx needs) was NOT FOUND") + print('Finished installing calculix (ccx)')
+ + +
[docs]def find_brew_binary_location(package_folder, search_string): + """Finds the location of a binary installed by homebrew""" + match_str = '/usr/local/Cellar/%s/**/*%s*' % (package_folder, + search_string) + paths = glob.glob(match_str, recursive=True) + for path in paths: + if os.access(path, os.X_OK): + return path + return None
+ +
[docs]def mac_remove(): + """Removes ccx and gmsh programs on mac, uses brew""" + brew_installed = shutil.which('brew') + if not brew_installed: + print('Installing brew') + url = 'https://raw.githubusercontent.com/Homebrew/install/master/install' + command_line = "/usr/bin/ruby -e \"$(curl -fsSL %s)\"" % url + subprocess.check_call(command_line, shell=True) + else: + print('brew present') + ccx_installed = shutil.which('ccx') + if not ccx_installed: + print('calculix (ccx) is not on your system') + else: + print('Removing calculix (ccx)') + # remove link to ccx + command_line = "rm /usr/local/bin/ccx" + subprocess.check_call(command_line, shell=True) + print('Calculix (ccx) symlink was removed') + # remove binary + command_line = "brew uninstall calculix-ccx" + subprocess.check_call(command_line, shell=True) + print('Calculix (ccx) binary was removed') + # remove gcc7 link if it exists + gcc7_sys_path = "/usr/local/opt/gcc/lib/gcc/7" + if os.path.islink(gcc7_sys_path): + command_line = "rm %s" % gcc7_sys_path + subprocess.check_call(command_line, shell=True) + print('gcc@7 symlink was removed (it was needed by calculix)') + # remove gcc@7 if it exists + gcc7_brew_path = "/usr/local/Cellar/gcc@7" + if os.path.isdir(gcc7_brew_path): + command_line = "brew uninstall gcc@7" + subprocess.check_call(command_line, shell=True) + print('gcc@7 was removed (it was needed by calculix)') + + gmsh_installed = shutil.which('gmsh') + if not gmsh_installed: + print('gmsh is not on your system') + else: + print('Removing gmsh') + command_line = "rm /usr/local/bin/gmsh" + subprocess.check_call(command_line, shell=True) + command_line = "rm -rf /Applications/Gmsh.app" + subprocess.check_call(command_line, shell=True)
+ + +
[docs]def windows_add(bitsize): + """Adds ccx and gmsh programs on windows""" + gmsh_installed = shutil.which('gmsh') + if not gmsh_installed: + print('Installing gmsh') + url = 'http://gmsh.info/bin/Windows/' + win_add_gmsh(bitsize, url, 'gmsh', '3.0.5') + else: + print('gmsh present') + + ccx_installed = shutil.which('ccx') + if not ccx_installed: + print('Installing calculix (ccx)') + url = 'https://sourceforge.net/projects/calculixforwin/files/03.2/' + win_add_ccx(bitsize, url, 'ccx') + else: + print('calculix (ccx) present')
+ +
[docs]def windows_remove(bitsize): + """Removes ccx and gmh programs on windows""" + gmsh_installed = shutil.which('gmsh') + if not gmsh_installed: + print('gmsh is not on your system') + else: + print('Removing gmsh') + env_path = os.getenv('VIRTUAL_ENV', sys.exec_prefix) + scripts_folder = '%s\Scripts\\' % env_path + remove_like(scripts_folder, 'gmsh') + ccx_installed = shutil.which('ccx') + if not ccx_installed: + print('ccx is not on your system') + else: + print('Removing ccx') + env_path = os.getenv('VIRTUAL_ENV', sys.exec_prefix) + scripts_folder = '%s\Scripts\\' % env_path + ccx_folder = '%sccx' % scripts_folder + add_remove_dll_links(ccx_folder, scripts_folder, add=False) + remove_like(scripts_folder, 'ccx')
+ +
[docs]def remove_like(search_path, name): + """ + Searches for files or folders matching name in search_path and deletes them + """ + match_str = '%s*%s*' % (search_path, name) + paths = glob.glob(match_str) + for path in paths: + print('Removing %s' % path) + if os.path.isfile(path): + os.unlink(path) + elif os.path.isdir(path): + shutil.rmtree(path)
+ +
[docs]def win_add_gmsh(bitsize, binaries_url, program_name, version_str): + """Installs a program from an apache server file listing page""" + # Needs the user agent header to exist for it to send back the content + user_agent = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) ' + 'Gecko/20100101 Firefox/56.0') + headers = {'User-Agent': user_agent} + + zipfile_regex = '.*%s-%s.*' % (program_name, version_str) + # Windows 10 64 bit, gmsh version: Gmsh 3.0.7-git-35176e26 hangs + zipfile_name = zipfile_by_bitsize(binaries_url, headers, zipfile_regex, + bitsize) + + zipfile_url = binaries_url + zipfile_name + print('Downloading %s from %s' % (program_name, zipfile_url)) + response = requests.get(zipfile_url, stream=True, headers=headers) + with open(zipfile_name, 'wb') as out_file: + for chunk in response.iter_content(chunk_size=1024): + if chunk: # filter out keep-alive new chunks + out_file.write(chunk) + print('Unzipping %s' % program_name) + zip_ref = zipfile.ZipFile(zipfile_name, 'r') + zipfile_folder_name = os.path.dirname(zip_ref.namelist()[0]) + zip_ref.extractall(None) + zip_ref.close() + print('Removing %s zipfile' % program_name) + os.remove(zipfile_name) + + env_path = os.getenv('VIRTUAL_ENV', sys.exec_prefix) + folder_to = '%s\Scripts\%s' % (env_path, zipfile_folder_name) + exe_loc = '%s\%s.exe' % (folder_to, program_name) + exe_link = '%s\Scripts\%s.exe' % (env_path, program_name) + paths = [exe_link, folder_to] + for path in paths: + if os.path.isfile(path): + os.unlink(path) + elif os.path.isdir(path): + shutil.rmtree(path) + print('Installing %s to %s' % (program_name, folder_to)) + shutil.move(zipfile_folder_name, folder_to) + os.link(exe_loc, exe_link)
+ +
[docs]def zipfile_by_bitsize(binaries_url, headers, zipfile_regex, bitsize): + """Returns the url linking to the correct zipfile""" + # this is used by ccx and gmsh + res = requests.get(binaries_url, headers=headers) + html = res.text + urls = re.findall(r'href=[\'"]?([^\'" >]+)', html) + pattern = re.compile(zipfile_regex) + urls = [url for url in urls if pattern.match(url)] + urls = urls[-2:] + url_choices = {32: urls[0], 64: urls[1]} + if 'win32' in urls[1] or 'Windows32' in urls[1]: + url_choices = {32: urls[1], 64: urls[0]} + return url_choices[bitsize]
+ + + +
[docs]def get_direct_url(url, headers): + """Gets the zip direct download link from the project download page""" + direct_download_url = href_from_link_text(url, + headers, + 'Problems Downloading') + parsed_download_url = urlparse(direct_download_url) + if parsed_download_url.scheme not in ['http', 'https']: + # url is relative, and is missing the scheme and netloc + parsed_parent_url = urlparse(url) + parsed_download_url = ParseResult(parsed_parent_url.scheme, + parsed_parent_url.netloc, + parsed_download_url.path, + parsed_download_url.params, + parsed_download_url.query, + parsed_download_url.fragment) + direct_download_url = parsed_download_url.geturl() + direct_download_url = href_from_link_text(direct_download_url, + headers, + 'direct link') + return direct_download_url
+ +
[docs]def win_add_ccx(bitsize, binaries_url, program_name): + """Installs ccx on a windows computer""" + # Needs the user agent header to exist for it to send back the content + user_agent = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) ' + 'Gecko/20100101 Firefox/56.0') + headers = {'User-Agent': user_agent} + + zipfile_regex = '.+CL.+win.+zip/download$' + zipfile_webpage_url = zipfile_by_bitsize(binaries_url, headers, + zipfile_regex, bitsize) + + zipfile_name = zipfile_webpage_url.split('/')[-2] + zipfile_url = get_direct_url(zipfile_webpage_url, headers) + + print('Downloading %s from %s' % (zipfile_name, zipfile_url)) + response = requests.get(zipfile_url, stream=True, headers=headers) + with open(zipfile_name, 'wb') as out_file: + for chunk in response.iter_content(chunk_size=1024): + if chunk: # filter out keep-alive new chunks + out_file.write(chunk) + print('Unzipping %s' % program_name) + zip_ref = zipfile.ZipFile(zipfile_name, 'r') + zipfile_folder_name = zip_ref.namelist()[0].split('/')[0] + zipfile_ccx_path = zipfile_folder_name + '/bin/ccx' + for member in zip_ref.namelist(): + if zipfile_ccx_path in member: + zip_ref.extract(member) + zip_ref.close() + print('Removing %s zipfile' % program_name) + os.remove(zipfile_name) + + folder_from = '%s\\bin\\ccx' % zipfile_folder_name + env_path = os.getenv('VIRTUAL_ENV', sys.exec_prefix) + scripts_path = '%s\Scripts' % env_path + folder_to = '%s\%s' % (scripts_path, program_name) + + binary_name = 'ccx212.exe' + exe_loc = '%s\%s' % (folder_to, binary_name) + exe_link = '%s\%s.exe' % (scripts_path, program_name) + paths = [exe_link, folder_to] + for path in paths: + if os.path.isfile(path): + os.unlink(path) + elif os.path.isdir(path): + shutil.rmtree(path) + + print('Installing %s to %s' % (program_name, folder_to)) + shutil.move(folder_from, folder_to) + shutil.rmtree(zipfile_folder_name) + add_remove_dll_links(folder_to, scripts_path, add=True) + os.link(exe_loc, exe_link)
+ + +
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/loads.html b/dist/documentation/_modules/pycalculix/loads.html new file mode 100644 index 0000000..3f20c26 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/loads.html @@ -0,0 +1,285 @@ + + + + + + + pycalculix.loads — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.loads

+"""This module stores load classes.
+"""
+from math import pi
+
+
[docs]class ConstLoad(object): + """Makes a load. Many types are supported. + + Possible load types: + - forces + - displacements + - thickness (on nodes) + - matl (on elements) + - pressure (or stress on faces) + - gravity (on elements) + - rotation about an axis (on elements) + + Args: + ltype (string): load type: + + - 'fx','fy','fz': force on each axis + - 'ux','uy','uz': displacement on each axis + - 'nodal_thickness': thickness on element nodes + - 'matl': matl on elements + - 'press': pressure, + is tension, - is compresion + - 'gravity': gravity in x axis direction, - goes towards y axis + - 'radps': radians per second rotation + - 'rpm': rotations per minute rotation + + comp (Component): component that the load is applied to + val (double or Matl): value of the load + Matl is needed when setting material loads + otherwise a double value is used to describe the load + """ + + def __init__(self, ltype, comp, val): + self.ltype = ltype + self.comp = comp + self.val = val + +
[docs] def get_list(self): + """Returns a list of [item, val] for plotting.""" + res = [] + if self.ltype == 'press': + # pressure + faces = self.comp.get_children() + for face in faces: + res.append([face, self.val]) + elif self.ltype in ['ux', 'uy', 'uz']: + nodes = self.comp.get_children() + load_dict = {self.ltype:self.val} + for node in nodes: + res.append([node, load_dict]) + return res
+ +
[docs] def ccx(self): + """Writes a load for Calculix ccx solver. + + Returns a list strings, where each item is a line to write + to a Calculix .inp text file. + """ + res = [] + cname = self.comp.name + if self.ltype == 'press': + # pressure + res.append('** Pressure on component: '+cname) + res.append('*DLOAD') + faces = self.comp.get_children() + for face in faces: + myline = '%i,P%i,%f' % (face.element.id, face.id, self.val) + res.append(myline) + elif self.ltype == 'gravity': + # gravity + res.append('** Gravity on component: '+cname) + res.append('*DLOAD') + res.append('%s,GRAV,%f,-1.,0.,0.' % (cname, self.val)) + elif self.ltype[0] == 'u': + # displacement + axis = self.ltype[1] + anum = {'x':1, 'y':2, 'z':3} + axis = anum[axis] + res.append('*BOUNDARY') + res.append('%s,%i,%i,%f' % (cname, axis, axis, self.val)) + elif self.ltype[0] == 'f': + # force + res.append('** Force on component: '+cname) + nodes = len(self.comp.items) + fval = self.val/nodes + axis = self.ltype[1] + anum = {'x':1, 'y':2, 'z':3} + axis = anum[axis] + res.append('*CLOAD') + res.append('%s,%i,%f' % (cname, axis, fval)) + elif self.ltype == 'nodal_thickness': + res.append('*NODAL THICKNESS') + res.append('%s, %f' % (cname, self.val)) + elif self.ltype == 'radps': + radsq = self.val**2 # square the speed in radians per sec + res.append('** Rotation force on component: '+cname) + res.append('** Speed = '+str(self.val)+' radians/sec') + res.append('*DLOAD') + res.append('%s,CENTRIF,%f,0.,0.,0.,0.,1.,0.' % (cname, radsq)) + elif self.ltype == 'rpm': + rad = 2.0*pi*(self.val/60.0) + radsq = rad**2 # square the speed in radians per sec + res.append('** Rotation force on component: '+cname) + res.append('** Speed = '+str(self.val)+' rotations/minute') + res.append('*DLOAD') + res.append('%s,CENTRIF,%f,0.,0.,0.,0.,1.,0.' % (cname, radsq)) + elif self.ltype == 'matl': + mat = self.val.name + res.append('*SOLID SECTION,ELSET=%s,MATERIAL=%s' % + (cname, mat)) + + return res
+ + +
[docs]class LinearLoad(object): + """Makes a load which varies depending on x, y, or z location. + + Load Equation: + Load(x) = const + mult*(xo - x) + + This load is used to set water pressure, where p = po + rho*g*(xo - x) + For water pressure this becomes: + + - P(x) = po + (rho*g)*(xo-x) + - P(x) = po + mult*(xo-x) + - g is positive, xo > x + + Args: + ltype (str): load type 'press_fluid' is the only valid option + comp (Component): component that the load acts on + const (float): constant term in load equation + mult (float): mult term in the load equation + xo (float): xo term in the load equation + axis (str): axis that the load depends on + 'x' or 'y' are valid options + """ + + def __init__(self, ltype, comp, const, mult, xo, axis='x'): + self.ltype = ltype + self.comp = comp + self.const = const + self.mult = mult + self.xo = xo + self.axis = axis + +
[docs] def get_val(self, xval): + """Returns the load function value at the given location. + + Args: + xval (float): the location where we want to find the load + """ + res = self.const + (self.xo - xval)*self.mult + return res
+ +
[docs] def get_list(self): + """Returns a list of [item, val] for plotting.""" + res = [] + faces = self.comp.get_children() + for face in faces: + xvals = [getattr(n, self.axis) for n in face.nodes] + xavg = sum(xvals)/len(xvals) + pval = self.get_val(xavg) + res.append([face, pval]) + return res
+ +
[docs] def ccx(self): + """Writes a load for Calculix ccx solver. + + Returns a list strings, where each item is a line to write + to a Calculix .inp text file. + """ + res = [] + res.append('** Fluid pressure on component: '+self.comp.name) + faces = self.comp.get_children() + for face in faces: + res.append('*DLOAD') + xvals = [getattr(n, self.axis) for n in face.nodes] + xavg = sum(xvals)/len(xvals) + pval = self.get_val(xavg) + myline = '%i,P%i,%f' % (face.element.id, face.id, pval) + res.append(myline) + return res
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/material.html b/dist/documentation/_modules/pycalculix/material.html new file mode 100644 index 0000000..29c483f --- /dev/null +++ b/dist/documentation/_modules/pycalculix/material.html @@ -0,0 +1,234 @@ + + + + + + + pycalculix.material — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.material

+"""This module stores material classes.
+"""
+
+from . import base_classes
+
+
[docs]class Material(base_classes.Idobj): + """Makes a linear elastic meterial. + + Args: + name (str): a unique matl name + + Attributes: + - id (int): material id number + - mechtype (str): defines mechanical material type: linear or non-linear material + - density (float): density in mass/volume units + - pratio (float): poisson's ratio (unitless) + - youngs (float): young's modulus in Force/area = stress units + - mechtpye (str): mechanical material type + options: 'linear' or 'nonlinear' + - exponent (float): exponent of Ramberg-Osgood stress-strain equation + - yield_stress (float): yield stress of material + - yield_offset (float): yield offset of Ramberg-Osgood stress-strain equation + - conductivity (float): thermal conductivity, Power / (distance-temp) + - spec_heat (float): specific heat (energy/(mass-temp) + - thermal_exp (dict): a dict storing temperature dependent thermal + -- expansion properties + + -- Thermal expansion is in strain per temperature + + - dict['data'] = zip of (temp, thermal_expansion) + - dict['tzero'] = the temperature zero point + """ + + def __init__(self, name): + self.name = name + base_classes.Idobj.__init__(self) + # mechanical + self.mechtype = None + self.density = None + self.youngs = None + self.pratio = None + self.exponent = None + self.yield_stress = None + self.yield_offset = None + # thermal + self.conductivity = None + self.spec_heat = None + # thermal growth + self.thermal_exp = {} + +
[docs] def set_mech_props(self, density, youngs, pratio, mechtype='linear', exponent=0., yield_stress=0., yield_offset=0.): + """Sets the mechanical properties: density, youngs, poisson_ratio. + + Args: + - density (float): density in mass/volume units + - pratio (float): poisson's ratio (unitless) + - youngs (float): young's modulus in Force/area = stress units + + Kargs: + - mechtpye (str): mechanical material type + options: 'linear' or 'nonlinear' + - exponent (float): exponent of Ramberg-Osgood stress-strain equation + - yield_stress (float): yield stress of material + - yield_offset (float): yield offset of Ramberg-Osgood stress-strain equation + """ + self.density = density + self.youngs = youngs + self.pratio = pratio + self.mechtype = mechtype + self.exponent = exponent + self.yield_stress = yield_stress + self.yield_offset = yield_offset
+ +
[docs] def set_therm_props(self, conductivity, spec_heat): + """Sets the thermal properties: conductivity, specifc_heat. + + Args: + - conductivity (float): Power / (distance-temp) + - spec_heat (float): specific heat (energy/(mass-temp) + """ + self.conductivity = conductivity + self.spec_heat = spec_heat
+ +
[docs] def set_therm_expan(self, alphas, temps=None, tzero=None): + """Sets the thermal expansion of the material. + + Args: + - alphas (float or list): list of thermal expansion alphas + length must be the same as the passed temps + - temps (list): list of temperatures + - tzero (float): temperature zero point + """ + self.thermal_exp = {} + isnum = (isinstance(alphas, float) or isinstance(alphas, int)) + if isinstance(alphas, list) and isinstance(temps, list): + self.thermal_exp['data'] = zip(temps, alphas) + elif temps == None and isnum: + self.thermal_exp['data'] = [alphas] + if tzero != None: + self.thermal_exp['tzero'] = tzero
+ +
[docs] def ccx(self): + """Returns a list of text strings for ccx defining the material.""" + res = [] + res.append('*MATERIAL,NAME='+self.name) + + if self.mechtype == 'linear': + res.append('*ELASTIC') + res.append(str(self.youngs)+','+str(self.pratio)) + elif self.mechtype == 'nonlinear': + res.append('*DEFORMATION PLASTICITY') + res.append(','.join([str(self.youngs),str(self.pratio),str(self.yield_stress),str(self.exponent),str(self.yield_offset)])) + if self.density != None: + res.append('*DENSITY') + res.append(str(self.density)) + if self.conductivity != None: + res.append('*CONDUCTIVITY') + res.append(str(self.conductivity)) + if self.spec_heat != None: + res.append('*SPECIFIC HEAT') + res.append(str(self.spec_heat)) + if self.thermal_exp != {}: + if 'tzero' in self.thermal_exp: + res.append('*EXPANSION,ZERO='+str(self.thermal_exp['tzero'])) + else: + res.append('*EXPANSION') + for pair in self.thermal_exp['data']: + if len(pair) == 1: + res.append(str(pair[0])) + else: + # temp, val + res.append('%f %f' % (pair[0], pair[1])) + return res
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/mesh.html b/dist/documentation/_modules/pycalculix/mesh.html new file mode 100644 index 0000000..514f269 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/mesh.html @@ -0,0 +1,537 @@ + + + + + + + pycalculix.mesh — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.mesh

+"""This module stores classes that make a finite element analysis mesh.
+"""
+from matplotlib.patches import Polygon  # needed for plotting elements
+from . import geometry
+
+
[docs]class Element(object): + """Makes a mesh element. + + Supports 4 noded quads, 8 noded quads, 3 noded tris, 6 noded tris. + + Args: + enum (int): element id number + ccxtype (str): ccx element type string, 4 characters long + + Description: element_shape + element_order + element_type + + - element_shape = 'quad' or 'tri' + - 'quad' = quadrangle + - 'tri' = triangle + - element_order = '1' or '2' + - '1' = corner nodes only (3 or 4 noded element) + - '2' = corner nodes and midside nodes (6 or 8 noded element) + - element_type = 'plstress' or 'plstrain' or 'axisym' + - 'plstress' = plane stress + - 'plstrain' = plane strain + - 'axisym' = axisymmetric + + axisymmetric + - 'CAX6' : 'tri2axisym' + - 'CAX3' : 'tri1axisym' + - 'CAX8' : 'quad2axisym' + - 'CAX4' : 'quad1axisym' + plane stress + - 'CPS6' : 'tri2plstress' + - 'CPS3' : 'tri1plstress' + - 'CPS8' : 'quad2plstress' + - 'CPS4' : 'quad1plstress' + plane strain + - 'CPE6' : 'tri2plstrain' + - 'CPE3' : 'tri1plstrain' + - 'CPE8' : 'quad2plstrain' + - 'CPE4' : 'quad1plstrain' + + nlist (list): list of Node, the nodes that define the element + + Attributes: + id (int): element id number + ccxtype (str): ccx element type string, 4 characters long + + Description: element_shape + element_order + element_type + + - element_shape = 'quad' or 'tri' + - 'quad' = quadrangle + - 'tri' = triangle + - element_order = '1' or '2' + - '1' = corner nodes only (3 or 4 noded element) + - '2' = corner nodes and midside nodes (6 or 8 noded element) + - element_type = 'plstress' or 'plstrain' or 'axisym' + - 'plstress' = plane stress + - 'plstrain' = plane strain + - 'axisym' = axisymmetric + + axisym + - 'CAX6' : 'tri2axisym' + - 'CAX3' : 'tri1axisym' + - 'CAX8' : 'quad2axisym' + - 'CAX4' : 'quad1axisym' + plane stress + - 'CPS6' : 'tri2plstress' + - 'CPS3' : 'tri1plstress' + - 'CPS8' : 'quad2plstress' + - 'CPS4' : 'quad1plstress' + plane strain + - 'CPE6' : 'tri2plstrain' + - 'CPE3' : 'tri1plstrain' + - 'CPE8' : 'quad2plstrain' + - 'CPE4' : 'quad1plstrain' + + node (dict): dictionary of nodes, keys are int >= 1 + face (dict): dictionary of faces, keys are int >= 1 + nodes (list): list of element nodes + faces (list): list of element faces + center (Point): the center point of the element + """ + + def __init__(self, enum, ccxtype, nlist): + self.id = enum + self.ccxtype = ccxtype + self.node = {} + self.face = {} + + # calculate the number of faces + fnum = len(nlist) + if fnum > 4: + fnum = int(fnum/2) + + # store nodes + for (ind, node) in enumerate(nlist): + if ind >= fnum: + node.set_order(2) + node.add_element(self) + self.node[ind+1] = node + + # store faces + nodes = nlist[0:fnum]+[nlist[0]] + for ind in range(fnum): + node1 = nodes[ind] + node2 = nodes[ind+1] + face = Face(ind+1, node1, node2, self) + if len(nlist) > fnum: + # we have second order nodes + face.set_nmid(self.node[ind+1+fnum]) + self.face[ind+1] = face + + # set the element center + self.center = self.calc_center() + + def __hash__(self): + """Returns the item's id as its hash.""" + return self.id + + @property + def faces(self): + """Returns list of element faces.""" + return self.face.values() + + @property + def nodes(self): + """Returns list of element nodes.""" + return self.node.values() + +
[docs] def get_poly(self): + """Returns a polygon of the element.""" + nodes = [node for node in self.nodes if node.order == 1] + # make a list of [[ax, rad],[... ] for making the polygon + xycoords = [[node.y, node.x] for node in nodes] + polygon = Polygon(xycoords, closed=True) + return polygon
+ +
[docs] def label(self, ax): + """Labels the element on the passed matplotlib axis.""" + ax.text(self.center.y, self.center.x, self.get_name(), + ha='center', va='center')
+ +
[docs] def calc_center(self): + """Returns the element center as a Point.""" + pts = [node for node in self.nodes if node.order == 1] + axials = [point.y for point in pts] + radials = [point.x for point in pts] + axial = sum(axials)/len(axials) + radial = sum(radials)/len(radials) + return geometry.Point(radial, axial)
+ +
[docs] def get_tris(self): + """Returns a list of triangles for plotting. Triangles are node ids. + + CCX elements are closed in a CCW direction relative to xy normal + CCX elements are closed in a CW direction relative to yx mine + ---> I have to draw in a clockwise direction, otherwise get error + Triangles are closed in a counter clockwise (CCW) direction to yx mine + http://matplotlib.org/api/tri_api.html#matplotlib.tri.Triangulation + """ + res = [] + numnodes = len(self.nodes) + if numnodes == 3: + # triangle, first order = 1 triangle + res.append([self.node[1].id, self.node[3].id, self.node[2].id]) + elif numnodes == 4: + # quad, first order = 2 triangles + res.append([self.node[1].id, self.node[3].id, self.node[2].id]) + res.append([self.node[1].id, self.node[4].id, self.node[3].id]) + elif numnodes == 6: + # tri, second order = 4 triangles + res.append([self.node[1].id, self.node[6].id, self.node[4].id]) + res.append([self.node[4].id, self.node[5].id, self.node[2].id]) + res.append([self.node[6].id, self.node[3].id, self.node[5].id]) + res.append([self.node[6].id, self.node[5].id, self.node[4].id]) + elif numnodes == 8: + # quad, second order = 6 triangles + res.append([self.node[1].id, self.node[8].id, self.node[5].id]) + res.append([self.node[5].id, self.node[6].id, self.node[2].id]) + res.append([self.node[6].id, self.node[7].id, self.node[3].id]) + res.append([self.node[8].id, self.node[4].id, self.node[7].id]) + res.append([self.node[8].id, self.node[7].id, self.node[6].id]) + res.append([self.node[8].id, self.node[6].id, self.node[5].id]) + return res
+ +
[docs] def get_area(self): + """Returns the element area.""" + asum = 0.0 + for face in self.faces: + node1 = face.nodes[0] + node2 = face.nodes[1] + asum += node2.y*node1.x - node2.x*node1.y + asum = asum*0.5 + return asum
+ +
[docs] def set_ccxtype(self, etype): + """Sets the ccx element type given an etype string. + + Args: + etype (string): element type string + + - 'plstress' = plane stress + - 'plstrain' = plane strain + - 'axisym' = axisymmetric + """ + fnum = len(self.faces) + shape = 'tri' + if fnum == 4: + shape = 'quad' + order = '2' + if len(self.nodes) == fnum: + order = '1' + estr = shape+order+etype + + # Calculix CCX elements + # axisymmetric + _ccx_elements = {} + _ccx_elements['tri2axisym'] = 'CAX6' + _ccx_elements['tri1axisym'] = 'CAX3' + _ccx_elements['quad2axisym'] = 'CAX8' + _ccx_elements['quad1axisym'] = 'CAX4' + # plane stress + _ccx_elements['tri2plstress'] = 'CPS6' + _ccx_elements['tri1plstress'] = 'CPS3' + _ccx_elements['quad2plstress'] = 'CPS8' + _ccx_elements['quad1plstress'] = 'CPS4' + # plane strain + _ccx_elements['tri2plstrain'] = 'CPE6' + _ccx_elements['tri1plstrain'] = 'CPE3' + _ccx_elements['quad2plstrain'] = 'CPE8' + _ccx_elements['quad1plstrain'] = 'CPE4' + # set element ccxtype + self.ccxtype = _ccx_elements[estr]
+ +
[docs] def ccx(self): + """Returns text string defining the element for a ccx input file.""" + nids = [str(n.id) for n in self.node.values()] + val = str(self.id)+', '+', '.join(nids) + return val
+ +
[docs] def get_name(self): + """Returns the element name based on id number.""" + name = 'E%i' % (self.id) + return name
+ + def __str__(self): + """Returns the element name.""" + nids = [str(node.id) for node in self.nodes] + return '%s nodes: %s' % (self.get_name(), ','.join(nids))
+ + +
[docs]class Face(object): + """Makes an element face. + + Args: + fnum (int): element face number + node1 (Node): first node + node2 (Node): second node + element (Element): parent element + + Attributes: + id (int): element face number + nodes (list): list of nodes [node1, node2] + nmid (Node or None): midside node, defaults to None + element (Element): parent element + ext (bool): stores if face is external + """ + + def __init__(self, fnum, node1, node2, element): + self.id = fnum + self.nmid = None + self.ext = False + self.element = element + self.nodes = [node1, node2] + for node in self.nodes: + node.add_face(self) + + def __hash__(self): + """Returns a hash = self.element.id*4, Lets us make sets of faces.""" + # we assume the largest number of faces is 4, face id is 1-4, need 0-3 + hval = self.element.id*4+(self.id-1) + return hval + +
[docs] def length(self): + """Return the length of the face.""" + point0 = geometry.Point(self.nodes[0].x, self.nodes[0].y) + point1 = geometry.Point(self.nodes[1].x, self.nodes[1].y) + vect = point1 - point0 + return vect.length()
+ +
[docs] def get_mnorm(self): + """Returns a list: [midpoint, normal_vector] + + Returns: + midpoint (Point): face midpoint + norm_vect (Point): face normal vector, it is a unit vector + """ + # return midpt, normal + point0 = geometry.Point(self.nodes[0].x, self.nodes[0].y) + point1 = geometry.Point(self.nodes[1].x, self.nodes[1].y) + midpt = point0 + point1 + midpt = midpt*0.5 + norm_vect = point1 - point0 + norm_vect.rot_ccw_deg(90) + norm_vect.make_unit() + return [midpt, norm_vect]
+ +
[docs] def set_ext(self): + """Sets the ext boolean flag to True. Face is external.""" + self.ext = True
+ +
[docs] def set_nmid(self, nmid): + """Sets the face midside node, nmid. + + Args: + nmid (Point): passed node to set as the face midside node + """ + self.nmid = nmid + nmid.add_face(self) + self.nodes = [self.nodes[0], self.nmid, self.nodes[-1]]
+ + def __eq__(self, other): + """Compare this face to other face. True if node lists equal. + + Args: + other (Face): the other face we are comparing + + Returns: + bool: True if face node lists are equal, False otherwise. + """ + if self.nodes == other.nodes: + return True + else: + return False + + def __str__(self): + """Returns string listing object type, id number and nodes.""" + node1 = self.nodes[0].id + node2 = self.nodes[1].id + fid = self.id + eid = self.element.id + tup = (fid, eid, node1, node2) + res = 'Face %i on element %i with nodes [%i,%i]' % tup + return res
+ + +
[docs]class Node(object): + """Makes a mesh node. + + Args: + node_num (int): node number + x (float): x-coordinate + y (float): y-coordinate + z (float): z-coordinate + + Attributes: + id (int): node number + x (float): x-coordinate + y (float): y-coordinate + z (float): z-coordinate + order (int): 1 or 2, 1 if it is a corner node, 2 if it is a midside node + elements (set): a set of elements that contain this node + faces (set): a set of faces that contain this node + """ + + def __init__(self, node_num, x, y, z): + self.id = node_num + self.x = x + self.y = y + self.z = z + self.order = 1 + self.elements = set() + self.faces = set() + +
[docs] def set_order(self, order): + """Sets the node order, must be 1 or 2. + + Args: + order (int): node order, 1 is corner node, 2 is midside node + """ + self.order = order
+ +
[docs] def add_element(self, element): + """Adds an element to this node's set of elements. + + Args: + element (Element): element to add to set self.elements + """ + self.elements.add(element)
+ +
[docs] def add_face(self, face): + """Adds a face to this node's set of faces. + + Args: + face (Face): face to add to set self.faces + """ + self.faces.add(face)
+ +
[docs] def label(self, ax): + """Labels the node on the passed Matplotlib axis.""" + ax.annotate(self.get_name(), (self.y, self.x))
+ + def __hash__(self): + """Returns the node id as the hash.""" + return self.id + + def __eq__(self, other): + """Returns boolean of id equality to other Node. + + Args: + other (Node): the node to compare to this node. + + Returns: + boolean: True if ids are equal, False if they are not + """ + if isinstance(other, Node): + return self.id == other.id + else: + return False + +
[docs] def ccx(self): + """Returns a string defining the node for a ccx inp file.""" + val = '%i, %f, %f, %f' % (self.id, self.x, self.y, self.z) + return val
+ +
[docs] def get_name(self): + """Returns the string node name.""" + name = 'N%i' % (self.id) + return name
+ + def __str__(self): + """Returns string listing object type, id number and (x,y) coords.""" + val = 'Node, id %i, (x,y)=(%f,%f)' % (self.id, self.x, self.y) + return val
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/partmodule.html b/dist/documentation/_modules/pycalculix/partmodule.html new file mode 100644 index 0000000..d99c39d --- /dev/null +++ b/dist/documentation/_modules/pycalculix/partmodule.html @@ -0,0 +1,1177 @@ + + + + + + + pycalculix.partmodule — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.partmodule

+"""This module stores the Part class. It is used to make 2D parts.
+"""
+import numpy as np # needed for linspace on hole creation
+
+from . import base_classes
+from . import geometry #point, line, area
+
+
[docs]class Part(base_classes.Idobj): + """This makes a part. + + Args: + parent: parent FeaModel + + Attributes: + __fea (FeaModel): parent FeaModel + points (list): list or part points, excludes arc centers + allpoints (list): list or part points, includes arc centers + lines (list): list of all Line and Arc that make the part + signlines (list): list of all SignLine and SignArc that make the part + __cursor (Point): location of a cursor drawing the part + __holemode (bool): if True, lines will be added to holes, otherwise, + they'll be added to areas + areas (list): list of Area that make up the part + left (list): list the parts leftmost lines, they must be vertical + right (list): list the parts rightmost lines, they must be vertical + top (list): list the parts top lines, they must be horizontal + bottom (list): list the parts bottom lines, they must be horizontal + center (Point): the area centroid of the part + nodes (list): list of part's nodes + elements (list): list of part's elements + """ + + def __init__(self, feamodel): + self.fea = feamodel + self.__cursor = geometry.Point(0, 0) + self.areas = [] # top area is the buffer + # make the buffer + area = self.fea.areas.append(geometry.Area(self, [])) + self.areas.append(area) + base_classes.Idobj.__init__(self) + self.left = None + self.right = None + self.top = None + self.bottom = None + self.center = None + self.nodes = [] + self.elements = [] + self.__holemode = False + self.fea.parts.append(self) + + def __hash__(self): + """Returns the item's id as its hash.""" + return self.id + + @property + def lines(self): + """Returns list of part lines.""" + lines = set() + for area in self.areas: + lines.update(area.lines) + return list(lines) + + @property + def signlines(self): + """Returns list of part signline and signarc.""" + lines = set() + for area in self.areas: + lines.update(area.signlines) + return list(lines) + + @property + def points(self): + """Returns list of part points, excludes arc centers.""" + points = set() + lines = self.lines + for line in lines: + points.update(line.points) + return list(points) + + @property + def allpoints(self): + """Returns list of part points, includes arc centers.""" + points = set() + lines = self.lines + for line in lines: + points.update(line.allpoints) + return list(points) + +
[docs] def get_item(self, item): + """"Returns the part's item(s) requested by the passed string. + + Args: + item (str): string requesting item(s) + + * Valid examples: 'P0', 'L0', 'left', 'A0' + + Returns: + item(s) or None: If items are found they are returned + + * If there is only one item it is returned + * If there are multiple items, they are returned as a list + * If no items are found None is returned + """ + if item in ['left', 'right', 'top', 'bottom']: + items = getattr(self, item) + return items + elif item[0] == 'P': + # get point + items = self.points + num = int(item[1:]) + res = [a for a in items if a.id == num] + return res[0] + elif item[0] == 'L': + # get line + items = self.signlines + num = int(item[1:]) + items = [a for a in items if a.id == num] + return items[0] + elif item[0] == 'A': + # get area + items = self.areas + num = int(item[1:]) + items = [a for a in items if a.id == num] + return items[0] + else: + print('Unknown item! Please pass the name of a point, line or area!') + return None
+ +
[docs] def get_name(self): + """Returns the part name based on id number.""" + return 'PART'+str(self.id)
+ + def __set_side(self, side): + """Sets the part.side to a list of lines on that side of the part. + + Used to set the part.left, part.right, part.top, part.bottom sides. + + Args: + side (string): 'left', 'right', 'top','bottom' + """ + # set index and axis, ind=0 is low side, ind=-1 is high side + inds = {'left':0, 'right':-1, 'top':-1, 'bottom':0} + axes = {'left':'y', 'right':'y', 'top':'x', 'bottom':'x'} + ind = inds[side] + axis = axes[side] + + # loc = 'left', ind = 0, axis = 'y' + points = self.points + # sort the points low to high + points = sorted(points, key=lambda pt: getattr(pt, axis)) + # store the target value + target_value = getattr(points[ind], axis) + res = [] + lines = self.signlines + for sline in lines: + if isinstance(sline, geometry.SignLine): + pt_axis_vals = [getattr(pt, axis) for pt in sline.points] + pt_dist_vals = [abs(target_value - pt_axis_val) for pt_axis_val in pt_axis_vals] + if all([pt_dist_val < geometry.ACC for pt_dist_val in pt_dist_vals]): + # line is on the left side + res.append(sline) + setattr(self, side, res) + +
[docs] def goto(self, x, y, holemode=False): + """Moves the part cursor to a location. + + If that location has a point at it, use it. + If not, make a new point at that location. + + Args: + x (float): x-coordinate of the point to go to + y (float): y-coordinate of the point to go to + holemode (bool): if True, we start drawing a hole here, otherwise + we start drawing an area + + Returns: + self.__cursor (Point): returns the updated cursor point + """ + [pnew, already_exists] = self.__make_get_pt(x, y) + + if already_exists: + if self.areas[-1].closed == True: + # make a new area if the old area is already closed and we're + # going to an existing point + area = self.fea.areas.append(geometry.Area(self, [])) + self.areas.append(area) + + # return cursor + self.__cursor = pnew + self.__holemode = holemode + return self.__cursor
+ + def __get_point(self, point): + """Returns point if found, None otherwise.""" + points = self.allpoints + found_point = None + for apoint in points: + dist = point - apoint + dist = dist.length() + if dist < geometry.ACC: + # point already exists in part, use it + found_point = apoint + break + return found_point + + def __make_get_pt(self, x, y): + """Gets a point if it exists, makes it if it doesn't. Returns the point. + + Use this when you need a point made in the part, and you want to + use an extant point if one is available. + + Args: + x (float): point x-coordinate + y (float): point y-coordinate + + Returns: + list: + list[0]: Point + list[1]: boolean, True = the point already existed + """ + thept = geometry.Point(x, y) + pfound = self.__get_point(thept) + pexists = True + if pfound == None: + pfound = thept + self.fea.register(pfound) + pexists = False + return [pfound, pexists] + + def __calc_area_center(self): + """Calculates and returns the part and centroid Point. + + Returns: + list: [area, Point] + """ + val_list = [] + for area in self.areas: + if area.closed == True: + aval, cval = area.area, area.center + val_list.append([aval, cval]) + a_sum = sum([aval[0] for aval in val_list]) + cxa_sum = sum([center.x*aval for [aval, center] in val_list]) + cya_sum = sum([center.y*aval for [aval, center] in val_list]) + cx_val = cxa_sum/a_sum + cy_val = cya_sum/a_sum + center = geometry.Point(cx_val, cy_val) + return [a_sum, center] + + def __make_get_sline(self, lnew): + """Returns a signed line or arc, makes it if it needs to. + + Args: + lnew (Line or Arc or SignLine or SignArc): Line or Arc to make + + Returns: + list: + list[0]: SignLine or SignArc + list[1]: boolean, True = the line already existed + """ + lpos = lnew.signed_copy(1) + lneg = lnew.signed_copy(-1) + # get part's signed lines + slines = self.signlines + lexists = False + for sline in slines: + if lpos == sline: + lexists = True + signline_new = sline + break + elif lneg == sline: + lexists = True + signline_new = sline.signed_copy(-1) + signline_new.edge = False + self.fea.register(signline_new) + break + else: + # fired when we haven't broken out of the loop, the line is new + if isinstance(lnew, geometry.SignLine): + lnew = geometry.Line(lnew.pt(0), lnew.pt(1)) + self.fea.register(lnew) + lnew.save_to_points() + signline_new = lnew.signed_copy(1) + self.fea.register(signline_new) + signline_new.line.add_signline(signline_new) + return [signline_new, lexists] + +
[docs] def draw_circle(self, center_x, center_y, radius, num_arcs=4): + """Draws a circle area and adds it to the part. + + Args: + center_x (float): x-axis hole center + center_y (float): y-axis hole center + radius (float): hole radius + num_arcs (int): number of arcs to use, must be >= 3 + + Returns: + loop (geometry.LineLoop): a LineLoop list of SignArc + """ + center = geometry.Point(center_x, center_y) + rvect = geometry.Point(0, radius) + start = center + rvect + self.goto(start.x, start.y) + angles = np.linspace(360/num_arcs,360,num_arcs, endpoint=True) + for ang in angles: + point = geometry.Point(0, radius).rot_ccw_deg(ang) + point = point + center + self.draw_arc(point.x, point.y, center.x, center.y) + loop = self.areas[-1].exlines + self.__update() + return loop
+ +
[docs] def draw_hole(self, center_x, center_y, radius, num_arcs=4, filled=False): + """Makes a hole in the part. + + Args: + center_x (float): x-axis hole center + center_y (float): y-axis hole center + radius (float): hole radius + num_arcs (int): number of arcs to use, must be >= 3 + filled (bool): whether to fill the hole + + * True: makes a new area in the part + + Returns: + hole_lines (list or None): list of hole SignLine or SignArc + + * Returns None if hole was not made. + """ + center = geometry.Point(center_x, center_y) + area = self.__area_from_pt(center) + if area == None: + print("You can't make a hole here until there's an area here!") + return None + else: + # make points + rvect = geometry.Point(0, radius) + start = center + rvect + self.goto(start.x, start.y, holemode=True) + angles = np.linspace(360/num_arcs,360,num_arcs, endpoint=True) + for ang in angles: + point = geometry.Point(0, radius).rot_ccw_deg(ang) + point = point + center + self.draw_arc(point.x, point.y, center.x, center.y) + # make new area + if filled: + # reverse order, reverse sline directions, store in feamodel + slines = list(area.holes[-1]) + slines.reverse() + slines = [sline.signed_copy(-1) for sline in slines] + slines = [self.__make_get_sline(sline)[0] for sline in slines] + anew = self.fea.areas.append(geometry.Area(self, slines)) + self.areas.append(anew) + self.__update() + return area.holes[-1]
+ +
[docs] def draw_arc_angle(self, degrees_ccw, center_x, center_y): + """Makes an arc and adds it to the part. + + | Current point is the first arc point. + | degrees_ccw is the swept angle in degrees, counterclockwise + | (center_x, center_y) is the arc center + + | Degrees: Traversed angle of arc must be < 180 degrees + + Args: + degrees_ccw (float): arc swept angle in degrees, counterclockwise + center_x (float): arc center point x-coordinate + center_y (float): arc center point y-coordinate + + Returns: + list: [arc, arc_start_point, arc_end_point] + """ + center = geometry.Point(center_x, center_y) + radius_vector = self.__cursor - center + radius_vector.rot_ccw_deg(degrees_ccw) + end = center + radius_vector + return self.draw_arc(end.x, end.y, center_x, center_y)
+ +
[docs] def draw_arc(self, end_x, end_y, center_x, center_y): + """Makes an arc and adds it to the part. + + | Current point is the first arc point. + | (end_x, end_y) is the end point + | (center_x, center_y) is the arc center + + | Degrees: Traversed angle of arc must be < 180 degrees + | Radians: Traversed angle of arc must be < Pi + + Args: + end_x (float): arc end point x-coordinate + end_y (float): arc end point y-coordinate + center_x (float): arc center point x-coordinate + center_y (float): arc center point y-coordinate + + Returns: + list: [arc, arc_start_point, arc_end_point] + """ + pold = self.__cursor + # make arc center point + ctr = self.__make_get_pt(center_x, center_y)[0] + # make arc end point + self.__cursor = self.__make_get_pt(end_x, end_y)[0] + + # make arc + arc = self.__make_get_sline(geometry.Arc(pold, self.__cursor, ctr))[0] + + if self.__holemode: + area = self.__area_from_pt(self.__cursor) + if area != None: + closed = area.add_hole_sline(arc) + if closed: + self.__holemode = False + else: + print('You must have a closed area here before making a hole!') + else: + self.areas[-1].add_sline(arc) + return [arc, pold, self.__cursor]
+ +
[docs] def draw_line_delta(self, delta_x, delta_y): + """Draws a line a relative distance, and adds it to the part. + + Args: + delta_x (float): x-axis delta distance to draw the line + delta_y (float): y-axis delta distance to draw the line + + Returns: + list: [line, point_start, point_end] + """ + x = self.__cursor.x + delta_x + y = self.__cursor.y + delta_y + return self.draw_line_to(x, y)
+ +
[docs] def draw_line_rad(self, dx_rad): + """Draws a line a relative radial distance, and adds it to the part. + + Args: + dx_rad (float): x-axis delta distance to draw the line + + Returns: + list: [line, point_start, point_end] + """ + return self.draw_line_delta(dx_rad, 0.0)
+ +
[docs] def draw_line_ax(self, dy_ax): + """Draws a line a relative axial distance, and adds it to the part. + + Args: + dy_ax (float): y-axis delta distance to draw the line + + Returns: + list: [line, point_start, point_end] + """ + return self.draw_line_delta(0.0, dy_ax)
+ +
[docs] def draw_line_to(self, x, y): + """Draws a line to the given location, and adds it to the part. + + Args: + x (float): x-axis coordinate of the end point + y (float): y-axis coordinate of the end point + + Returns: + list: [SignLine, point_start, point_end] + """ + pold = self.__cursor + self.__cursor = self.__make_get_pt(x, y)[0] + sline = self.__make_get_sline(geometry.Line(pold, self.__cursor))[0] + + if self.__holemode: + area = self.__area_from_pt(self.__cursor) + if area != None: + closed = area.add_hole_sline(sline) + if closed: + self.__holemode = False + self.__update() + else: + print('You must have a closed area here before making a hole!') + else: + # drawing in last area + self.areas[-1].add_sline(sline) + # check for closure of the area + if self.areas[-1].closed: + self.__update() + return [sline, pold, self.__cursor]
+ + def __get_maxlength(self): + """Returns the max distance between points in the part.""" + points = self.points + maxlen = 0.0 + # loop through points checking dist to next point + for ind, point_1 in enumerate(points[:-1]): + for point_2 in points[ind:]: + vect = point_1 - point_2 + dist = vect.length() + if dist > maxlen: + maxlen = dist + return maxlen + + def __area_from_pt(self, point): + """Returns the area that the point is inside. + + Args: + point (Point): the point we are asking about + + Returns: + Area or None: + Area is the found area + None is returned if the point is not in one of this part's areas + """ + for area in self.areas: + if area.contains_point(point): + return area + return None + +
[docs] def fillet_lines(self, line1, line2, radius): + """Fillets the given lines in the part. + + This inserts an arc in the part tangent to the two given lines. + + Args: + line1 (SignLine): line that the arc starts on, arc is tangent + line2 (SignLine): line that the arc ends on, arc is tangent + radius (float): arc radius size + + Returns: + list: [arc, start_point, end_point] + """ + # check if the lines are touching + if not line1.line.touches(line2.line): + print('ERROR: Cannot fillet! Lines must touch!') + return + + if line1.line.pt(1) == line2.line.pt(0): + first_line = line1 + second_line = line2 + elif line2.line.pt(1) == line1.line.pt(0): + first_line = line2 + second_line = line1 + else: + print('ERROR: Sign lines must both be going in CW or CCW ' + 'direction. The two passed lines are going in ' + 'different directions. Unable to fillet them.') + return + + tmp = self.__cursor + # offset the lines, assuming area is being traced clockwise + # get the intersection point + magnitude = radius + l1_off = first_line.offset(magnitude) + l2_off = second_line.offset(magnitude) + ctrpt = l1_off.intersects(l2_off) + if ctrpt == None: + # flip the offset direction if lines don't intersect + magnitude = -radius + l1_off = first_line.offset(magnitude) + l2_off = second_line.offset(magnitude) + ctrpt = l1_off.intersects(l2_off) + + # now we have an intersecting point + p1_new = first_line.arc_tang_intersection(ctrpt, magnitude) + p2_new = second_line.arc_tang_intersection(ctrpt, magnitude) + rempt = first_line.pt(1) + + p1_new = self.__make_get_pt(p1_new.x, p1_new.y)[0] + ctrpt = self.__make_get_pt(ctrpt.x, ctrpt.y)[0] + p2_new = self.__make_get_pt(p2_new.x, p2_new.y)[0] + + # make the new arc + arc = self.__make_get_sline(geometry.Arc(p1_new, p2_new, ctrpt))[0] + + # put the arc in the right location in the area + area = first_line.lineloop.parent + area.line_insert(first_line, arc) + print('Arc inserted into area %i' % (area.id)) + + # edit the adjacent lines to replace the removed pt + first_line.set_pt(1, arc.pt(0)) + second_line.set_pt(0, arc.pt(1)) + # del old pt, store new points for the arc + self.fea.points.remove(rempt) + + # reset the cursor to where it should be + self.__cursor = tmp + return [arc, arc.pt(0), arc.pt(1)]
+ +
[docs] def fillet_all(self, radius): + """Fillets all external lines not within 10 degrees of tangency + + Args: + radius (float): the fillet radius to use + + Returns: + arcs (list): list of SignArc + """ + pairs = [] + for area in self.areas: + for ind, sline in enumerate(area.exlines): + prev_sline = area.exlines[ind-1] + this_point = sline.pt(0) + if len(this_point.lines) == 2: + # only fillet lines that are not shared by other areas + if (isinstance(sline, geometry.SignLine) + and isinstance(prev_sline, geometry.SignLine)): + # only fillet lines + perp1 = prev_sline.get_perp_vec(this_point) + perp2 = sline.get_perp_vec(this_point) + ang = perp1.ang_bet_deg(perp2) + is_tangent = (-10 <= ang <= 10) + if is_tangent == False: + pairs.append([prev_sline, sline]) + arcs = [] + for pair in pairs: + arc = self.fillet_lines(pair[0], pair[1], radius)[0] + arcs.append(arc) + return arcs
+ +
[docs] def label(self, axis): + """Labels the part on a Matplotlib axis + + Args: + axis (Matplotlib Axis): Matplotlib Axis + """ + axis.text(self.center.y, self.center.x, self.get_name(), + ha='center', va='center')
+ +
[docs] def plot(self, axis, label=True, color='yellow'): + """Plots the part on the passed Matplotlib axis. + + Args: + axis (Matplotlib axis): the axis we will plot on + label (bool): True displays the part label + color (tuple): r,g,b,a matplotlib color tuple + """ + patches = [] + for area in self.areas: + if area.closed: + patches.append(area.get_patch()) + for patch in patches: + patch.set_color(color) + axis.add_patch(patch) + # apply the label + if label: + self.label(axis)
+ + def __cut_line(self, point, line): + """Cuts the passed line at the passed point. + + The passed line is cut into two lines. All areas that included the + original line are updated. + + Args: + line (Line or Arc): the line to cut, must be Line or Arc + point (Point): the location on the line that we will cut it + + Returns: + list: [pnew, lnew] + pnew: the new point we created to cut the original line + lnew: the new line we created, the end half of the orignal line + """ + pnew = self.__make_get_pt(point.x, point.y)[0] + if point.id != -1: + # if passed point already exists, use it + pnew = point + pend = line.pt(1) + line.set_pt(1, pnew) # shortens the line + new_prim = geometry.Line(pnew, pend) + if isinstance(line, geometry.Arc): + new_prim = geometry.Arc(pnew, pend, line.actr) + new_sline = self.__make_get_sline(new_prim)[0] + # insert the new line into existing areas + is_line = isinstance(line, geometry.Line) or isinstance(line, geometry.Arc) + is_sline = isinstance(line, geometry.SignLine) or isinstance(line, geometry.SignArc) + print('Cutting line (is_line, is_sline, signlines) (%s, %s, %i)' % (is_line, is_sline, len(line.signlines))) + slines = line.signlines + for sline in slines: + area = sline.lineloop.parent + if sline.sign == 1: + # cutting line in clockwise area, where line is pos + area.line_insert(sline, new_sline) + elif sline.sign == -1: + # cutting line in clockwise area, where line is neg + rev_sline = self.__make_get_sline(new_sline.signed_copy(-1))[0] + area.line_insert(sline, rev_sline, after=False) + return [pnew, new_sline] + + def __cut_area(self, area, start_pt, end_pt): + """Cuts the part area from start_pt to end_pt.""" + # select the line portions that define the areas + # list[:low] excludes low index + # list [high:] includes high index + # we want the line which start with the point + lpre_start = area.line_from_startpt(start_pt) + lpre_end = area.line_from_startpt(end_pt) + if lpre_start == None or lpre_end == None: + self.fea.plot_geometry() + print(area.exlines) + istart = area.exlines.index(lpre_start) + iend = area.exlines.index(lpre_end) + low = min(istart, iend) + high = max(istart, iend) + + # lists of lines for areas + beg = area.exlines[:low] + mid = area.exlines[low:high] + end = area.exlines[high:] + + # make cut line for [beg + cut + end] area + start_pt = mid[0].pt(0) + end_pt = mid[-1].pt(1) + fwd = geometry.Line(start_pt, end_pt) + rev = geometry.Line(end_pt, start_pt) + + # update existing area + cline = self.__make_get_sline(fwd)[0] + alist_curr = beg + [cline] + end + area.update(alist_curr) + + # make new area + cline_rev = self.__make_get_sline(rev)[0] + alist_other = mid + [cline_rev] + anew = geometry.Area(self, alist_other) + self.fea.register(anew) + self.areas.append(anew) + + # fix holes + self.__store_holes() + + def __merge_hole(self, area, start_pt, end_pt): + """Merges the hole into its area with a line between passed points.""" + # line will be drawn from start point on exlines to end point on hole + hole_points = area.holepoints + if start_pt in hole_points: + tmp = start_pt + start_pt = end_pt + end_pt = tmp + lpre_start = area.line_from_startpt(start_pt) + hole_line = area.line_from_startpt(end_pt) + if lpre_start == None or hole_line == None: + self.fea.plot_geometry() + ind = area.exlines.index(lpre_start) + + # store sections of the area + beg = area.exlines[:ind] + end = area.exlines[ind:] + thehole = None + mid = [] + for hole in area.holes: + for sline in hole: + if sline == hole_line: + ind = hole.index(sline) + mid = hole[ind:] + hole[:ind] + thehole = hole + break + if mid != []: + break + + fwd = geometry.Line(start_pt, end_pt) + fwd_sline = self.__make_get_sline(fwd)[0] + rev_sline = fwd_sline.signed_copy(-1) + self.fea.register(rev_sline) + rev_sline.line.add_signline(rev_sline) + alist_curr = beg + [fwd_sline] + mid + [rev_sline] + end + area.holes.remove(thehole) + area.update(alist_curr) + + def __get_cut_line(self, cutline): + """Returns a cut line beginning and ending on the part.""" + # find all intersections + lines = self.lines + points = set() + + # add line intersections + for line in lines: + newpt = line.intersects(cutline) + if newpt != None: + points.add(newpt) + + # loop through intersection points, storing distance + points = list(points) + for (ind, point) in enumerate(points): + dist = point - cutline.pt(0) + dist = dist.length() + pdict = {'dist': dist, 'point': point} + points[ind] = pdict + + # sort the points by dist, lowest to highest, return first cut + points = sorted(points, key=lambda k: k['dist']) + start = points[0]['point'] + end = points[1]['point'] + new_cut = geometry.Line(start, end) + return new_cut + + def __cut_with_line(self, cutline, debug): + """Cuts the part using the passed line. + + Args: + cutline (Line): line to cut the area with + debug (list): bool for printing, bool for plotting after every cut + """ + # find all intersections + lines = self.lines + points = set() + + # add line intersections + for line in lines: + if debug[0]: + print('Checking X between %s and cutline' % line.get_name()) + newpt = line.intersects(cutline) + if debug[0]: + print(' Intersection: %s' % newpt) + if newpt != None: + points.add(newpt) + + # loop through intersection points, storing distance and lines to cut + points = list(points) + for (ind, point) in enumerate(points): + dist = point - cutline.pt(0) + dist = dist.length() + pdict = {'dist': dist} + realpt = self.__get_point(point) + # we only want to store lines to cut here + if realpt == None or realpt.arc_center == True: + # we could have an existing arc center on a line that needs to + # be cut + if realpt == None: + realpt = point + for line in lines: + point_on_line = line.coincident(realpt) + if point_on_line and point not in line.points: + pdict['line'] = line + break + pdict['point'] = realpt + points[ind] = pdict + + # sort the points by dist, lowest to highest + points = sorted(points, key=lambda k: k['dist']) + if debug[0]: + print('==================================') + print('Points on the cutline!------------') + for pdict in points: + print(pdict['point']) + print(' dist %.3f' % pdict['dist']) + if 'line' in pdict: + print(' X cut line: %s' % pdict['line']) + print('==================================') + + # loop through the points cutting areas + for ind in range(len(points)): + pdict = points[ind] + start_pt = pdict['point'] + if 'line' in pdict: + # cut the line and point to the real new point + print('Cut through line %s' % pdict['line'].get_name()) + pnew = self.__cut_line(start_pt, pdict['line'])[0] + points[ind]['point'] = pnew + start_pt = pnew + end_pt = None + pavg = None + area = None + if ind > 0: + # find the area we're working on + end_pt = points[ind-1]['point'] + pavg = start_pt + end_pt + pavg = pavg*0.5 + area = self.__area_from_pt(pavg) + + if area == None: + # stop cutting if we are trying to cut through a holes + print('No area found at point avg, no cut made') + break + start_hole = start_pt in area.holepoints + end_hole = end_pt in area.holepoints + if start_hole and end_hole and area != None: + print('Trying to join holes, no cut made') + break + # stop cutting if we are trying to join holes + if end_hole == True or start_hole == True: + print('Merging hole in %s' % area.get_name()) + self.__merge_hole(area, start_pt, end_pt) + else: + print('Cutting %s' % area.get_name()) + self.__cut_area(area, start_pt, end_pt) + if debug[1]: + self.fea.plot_geometry() + + def __store_holes(self): + """Puts all holes in their correct areas""" + holes = [] + for area in self.areas: + holes += area.holes + for hole in holes: + hole_area = hole.parent + for area in self.areas: + is_inside = hole.inside(area.exlines) + if is_inside == True: + if area != hole_area: + # delete the hole from the old area, move it to the new + hole.set_parent(area) + hole_area.holes.remove(hole) + hole_area.close() + area.holes.append(hole) + area.close() + afrom, ato = hole_area.get_name(), area.get_name() + print('Hole moved from %s to %s' % (afrom, ato)) + + def __vect_to_line(self, point, cvect): + """Returns a cutting line at a given point and cutting vector. + + Args: + point (Point): the location we are cutting from + cvect (Point): the vector direction of the cut from pt + + Returns: + cutline (Line): cut line + """ + cvect.make_unit() + vsize = self.__get_maxlength() + endpt = point + cvect*vsize + cutline = geometry.Line(point, endpt) + cutline = self.__get_cut_line(cutline) + return cutline + + def __chunk_area(self, area, mode, exclude_convex, debug): + """Cuts the passed area into regular smaller areas. + + The cgx mesher only accepts areas which are 3-5 sides + so one may need to call this before using that mesher. + Cuts are made perpendicular to tangent points or at + internal corners. + At internal corners two perpendicular cuts are made. + + Args: + area (Area): the area to cut into smaller areas + mode (str): 'both', 'holes' or 'ext' chunks the area using the + points form this set. See part.chunk + exclude_convex (bool): If true exclude cutting convex tangent points + debug (list): bool for printing, bool for plotting after every cut + """ + # store the cuts first, then cut after + cuts = [] # each item is a dict with a pt and vect in it + loops = [] + cut_point_sets = [] + if mode == 'holes': + loops = area.holes + elif mode == 'ext': + loops = [area.exlines] + elif mode == 'both': + loops = area.holes + [area.exlines] + for loop in loops: + for ind, line in enumerate(loop): + line_pre = loop[ind-1] + line_post = line + point = line_pre.pt(1) + perp1 = line_pre.get_perp_vec(point) + perp2 = line_post.get_perp_vec(point) + #tan1 = line_pre.get_tan_vec(point) + #tan2 = line_post.get_tan_vec(point) + # flip these vectors later to make them cut the area(s) + ang = perp1.ang_bet_deg(perp2) + cut = {} + make_cut = True + pre_arc = isinstance(line_pre, geometry.SignArc) + post_arc = isinstance(line_post, geometry.SignArc) + if pre_arc or post_arc: + if pre_arc and post_arc: + if (line_pre.concavity == 'convex' + and line_post.concavity == 'convex' + and exclude_convex == True): + make_cut = False + else: + # only one is an arc + arc = line_pre + if post_arc: + arc = line_post + if arc.concavity == 'convex' and exclude_convex == True: + make_cut = False + is_tangent = (-10 <= ang <= 10) + is_int_corner = (45 <= ang <= 135) + """ + print('-------------------') + print('%s' % point) + print('Angle is %.3f' % ang) + print('Make cut %s' % make_cut) + print('is_tangent %s' % is_tangent) + print('is_int_corner %s' % is_int_corner) + """ + if is_tangent: + if make_cut == True: + # tangent + cut = {'pt':point, 'vect':perp1*-1} + cut_line = self.__vect_to_line(cut['pt'], cut['vect']) + pset = set(cut_line.points) + if pset not in cut_point_sets: + cut_point_sets.append(pset) + cut['line'] = cut_line + cuts.append(cut) + elif is_int_corner: + # internal corner + cut = {'pt':point, 'vect':perp1*-1} + cut_line = self.__vect_to_line(cut['pt'], cut['vect']) + pset = set(cut_line.points) + if pset not in cut_point_sets: + cut_point_sets.append(pset) + cut['line'] = cut_line + cuts.append(cut) + cut = {'pt':point, 'vect':perp2*-1} + cut_line = self.__vect_to_line(cut['pt'], cut['vect']) + pset = set(cut_line.points) + if pset not in cut_point_sets: + cut_point_sets.append(pset) + cut['line'] = cut_line + cuts.append(cut) + elif ang < 0: + # external corner + # do not split these + pass + + # do the cuts + for cut in cuts: + print('--------------------') + print('Cut point:', cut['pt'].get_name()) + print('Cut line:', cut['line']) + self.__cut_with_line(cut['line'], debug) + +
[docs] def chunk(self, mode='both', exclude_convex = True, debug=[0, 0]): + """Chunks all areas in the part. + + Args: + mode (str): area chunking mode + + - 'both': cuts areas using holes and exterior points + - 'holes': cut areas using holes points only + - 'ext': cut areas using exterior points only + + exclude_convex (bool): If true exclude cutting convex tangent points + """ + for area in self.areas: + if area.closed: + min_sides = 5 + has_holes = len(area.holes) > 0 + ext_gr = len(area.exlines) > min_sides + both_false = (has_holes == False and ext_gr == False) + if mode == 'holes' and has_holes: + self.__chunk_area(area, mode, exclude_convex, debug) + elif (mode == 'both' + and (has_holes or ext_gr or not exclude_convex)): + self.__chunk_area(area, mode, exclude_convex, debug) + elif mode == 'ext' and (ext_gr or not exclude_convex): + self.__chunk_area(area, mode, exclude_convex, debug) + else: + aname = area.get_name() + val = 'Area %s was not chunked because it had' % aname + adder = '' + if mode == 'both' and both_false: + adder = '<= %i lines and no holes.' % min_sides + elif has_holes == False and (mode in ['both', 'holes']): + adder = 'no holes.' + elif ext_gr == False and (mode in ['both', 'ext']): + adder = '<= %i lines.' % min_sides + print('%s %s' % (val, adder)) + + # store the left, right, top, and bottom lines + self.__update()
+ + def __update(self): + """Updates the left, right, top, bottom sides and area and center""" + self.__set_side('left') + self.__set_side('right') + self.__set_side('top') + self.__set_side('bottom') + self.area, self.center = self.__calc_area_center() + + def __str__(self): + """Returns string listing object type, id number and name.""" + val = 'Part, id=%i name=%s' % (self.id, self.get_name()) + return val
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/problem.html b/dist/documentation/_modules/pycalculix/problem.html new file mode 100644 index 0000000..c880320 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/problem.html @@ -0,0 +1,388 @@ + + + + + + + pycalculix.problem — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.problem

+"""This module stores the Problem class which is used to solve different types
+of analysis.
+"""
+
+import os # used to see if there is a results file
+import subprocess # used to launch ccx solver
+
+from . import environment
+from . import base_classes
+from . import results_file
+
+
[docs]class Problem(base_classes.Idobj): + """Makes a problem which can be analyzed with Calculix ccx. + + Args: + - feamodel (FeaModel): the parent FeaModel + - problem_type (str): model type, options: + -- 'struct': structural + - fname (str): file prefix for the problem .inp and results files + If value is '' it will default to the project name of the FeaModel + + Attributes: + fea (FeaModel): parent FeaModel + __ptype (str): problem type, options: + + - 'struct': structural + solved (bool): boolean storing whether or not the analysis was solved + fname (str): the problem database and results file prefix + rfile (Results_File): + Results_File is loaded in after the model has been solved + """ + def __init__(self, feamodel, problem_type, fname=''): + self.fea = feamodel + self.__ptype = problem_type + self.solved = False + if fname == '': + fname = self.fea.fname + self.fname = fname + self.rfile = results_file.ResultsFile(self) + base_classes.Idobj.__init__(self) + self.fea.problems.append(self) + + @staticmethod + def __get_ntxt(nodes): + """Returns list of strings defining all nodes. + + Args: + nodes (list): list of all nodes + """ + res = [] + res.append('*NODE, NSET=nodes') + for node in nodes: + res.append(node.ccx()) + print('INFO: %i nodes' % len(nodes)) + return res + + def __get_etxt(self, elements): + """Returns list of strings defining all elements. + + Args: + elements (list): list of all elements + """ + res = [] + ccxtypes = set([e.ccxtype for e in elements]) + eall_written = False + eall = [] + for ccxtype in ccxtypes: + setname = ccxtype + if len(ccxtypes) == 1: + setname = 'EAll' + eall_written = True + res.append('*ELEMENT, TYPE=%s, ELSET=%s' % (ccxtype, setname)) + elist = [e for e in elements if e.ccxtype == ccxtype] + print('INFO: %i %s elements' % (len(elist), ccxtype)) + for element in elist: + res.append(element.ccx()) + eall += elist + print('INFO: %i total elements' % len(eall)) + if eall_written == False: + tmp = self.__get_eset('EALL', eall) + res += tmp + return res + + @staticmethod + def __get_ctxt(components): + """Returns list of strings defining all components. + + Args: + components (list): list of all components + """ + res = [] + for comp in components: + res += comp.ccx() + return res + + @staticmethod + def __get_eset(name, elements): + """Returns list of strings defining components of elements. + + Args: + name (str): component name + elements (list): list of component elements + """ + res = [] + items_per_line = 6 + res.append('*ELSET,ELSET='+name) + grouped_els = base_classes.chunk_list(elements, items_per_line) + for group in grouped_els: + item_ids = [str(e.id) for e in group] + line = ', '.join(item_ids) + if group != grouped_els[-1]: + line += ',' + res.append(line) + return res + + @staticmethod + def __fix_line(line, fstr): + """Fixes the line and returns a fixed line. + + Args: + line (str): line to fix + fstr (str): format string to use + """ + items = fstr.split(',') + node_num_size = int(items[2][1:]) + pre_len = 3 + node_num_size + # we are missing + prefix on some numbers, some are too long + start_str = line[:pre_len] + end_str = line[pre_len:] + fields = end_str.count('E') + low_ind = 0 + values = [] + while len(values) < fields: + high_ind = end_str.find('E', low_ind)+5 + value = float(end_str[low_ind:high_ind]) + values.append(value) + low_ind = high_ind + values = ['%12.5e' % val for val in values] + end_str = ''.join(values) + new_str = start_str + end_str + '\n' + return new_str + + def __fix_frd(self): + """Fixes the frd file on win32 systems. Text formatting fixed + + On win32 Calculix, results file formatting is not written correctly. + Nodal results and stresses are not written as fixed length fields. + """ + if 'win32' in environment.CCX: + frd_file = self.fname+'.frd' + lines = [] + try: + with open(frd_file, "r") as infile: + lines = infile.readlines() + numlines = len(lines) + ind = 0 + fix = False + fstr = '' + while ind < numlines: + line = lines[ind] + if '1PSTEP' in line: + # we are in a results block + ind += 1 + line = lines[ind] + format_ = int(line.split()[-1]) + if format_ == 0: + fstr = "1X,I2,I5,6E12.5" + elif format_ == 1: + fstr = "1X,I2,I10,6E12.5" + ind += 1 + line = lines[ind] + if 'DISPR' in line or 'FORCR' in line: + ind += 5 + else: + ind += 7 + fix = True + line = lines[ind] + if line[:3] == ' -3': + fix = False + if fix: + lines[ind] = self.__fix_line(line, fstr) + ind += 1 + with open(frd_file, "w") as outfile: + outfile.writelines(lines) + print('File %s had its formatting fixed!' % frd_file) + except IOError: + print('Error reading .frd file!') + +
[docs] def solve(self): + """Solves the model in Calculix ccx.""" + inp = [] + + # store what results we'll be outputting for each type of analysis + out_el = {} + out_el['struct'] = 'E,S' # strain, stress + out_node = {} + out_node['struct'] = 'RF,U' # reaction forces, displacement + + if self.__ptype == 'struct': + # store nodes, elements, components + box = {} + box['nodes'] = [] + box['elements'] = [] + box['components'] = set() + + # store all loads in the parts in this model + load_dict = self.fea.loads + + # store all nodes, elements, and part element sets + box['nodes'] = self.fea.view.nodes + box['elements'] = self.fea.view.elements + + # store all node and element components + for time in load_dict: + for load in load_dict[time]: + if load.ltype not in ['press', 'press_fluid']: + box['components'].add(load.comp) + for contact in self.fea.contacts: + box['components'].add(contact.master_comp) + box['components'].add(contact.slave_comp) + + box['nodes'] = self.__get_ntxt(box['nodes']) + box['elements'] = self.__get_etxt(box['elements']) + box['components'] = self.__get_ctxt(box['components']) + + # add text definition for nodes, elelents, components + inp += box['nodes']+[''] + inp += box['elements']+[''] + inp += box['components']+[''] + + # read in all materials + for matl in self.fea.matls: + inp += matl.ccx() + + # write all steps and loads + for time in sorted(load_dict.keys()): + if time == 0.0: + # this is for thicknesses and materials + for load in load_dict[time]: + inp += load.ccx() + for surf_interaction in self.fea.surfints: + inp += surf_interaction.ccx() + for contact in self.fea.contacts: + inp += contact.ccx() + else: + # only write times >= 1 + inp.append('*STEP') + inp.append('*STATIC') + + for load in load_dict[time]: + inp += load.ccx() + + # make output frd file for cgx + inp.append('*EL FILE') + inp.append(out_el[self.__ptype]) + inp.append('*NODE FILE') + inp.append(out_node[self.__ptype]) + + # make output dat file for integration point results + inp.append('*EL PRINT,ELSET=EALL') + inp.append('S') + + # end step + inp.append('*END STEP') + + # write CCX inp file to the local directory + fname = self.fname+'.inp' + with open(fname, 'w') as outfile: + for line in inp: + #print (line) + outfile.write(line+'\n') + print('File: %s was written' % (fname)) + + # run file + runstr = "%s %s" % (environment.CCX, self.fname) + print(runstr) + subprocess.check_call(runstr, shell=True) + print('Solving done!') + + # select the probem's parts and load the results file + frd_file = self.fname+'.frd' + if os.path.isfile(frd_file): + self.solved = True + self.__fix_frd() + self.rfile.load() + else: + print('ERROR: results .frd file was not written!')
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/results_file.html b/dist/documentation/_modules/pycalculix/results_file.html new file mode 100644 index 0000000..3d2070c --- /dev/null +++ b/dist/documentation/_modules/pycalculix/results_file.html @@ -0,0 +1,1349 @@ + + + + + + + pycalculix.results_file — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.results_file

+"""This module stores the Results_File class."""
+
+import collections
+import math # used for metric number conversion
+import os #need to check if results file exists
+import re # used to get info from frd file
+import subprocess # used to check ccx version
+
+import matplotlib.pyplot as plt
+import matplotlib.colors as colors
+import matplotlib.cm as cmx
+import numpy as np
+from numpy.lib.polynomial import roots # need to find S1-S3
+from numpy.core.function_base import linspace # need to make contours
+
+
+from . import base_classes # needed for RESFIELDS
+from . import environment
+from . import mesh
+
+CMAP = 'jet'
+
+
[docs]class ResultsFile(object): + """Makes a results file. + + Args: + problem (Problem): problem that was solved + + Attributes: + __problem (Problem): parent problem + __steps (list): a list of float time steps + __results (dict): a dict storing the results data + results[step]['node'][nnum][field] --> value + results[step]['element'][enum]['avg'][field] --> value + results[step]['element'][enum]['max'][field] --> value + results[step]['element'][enum]['min'][field] --> value + results[step]['element'][enum]['ipoints'][ipnum][field] --> value + field = 'ux' or 'Seqv' or 'ey' etc. + __time (float): current time we are looking at, defaults to -1 + When a file is loaded in, the first time step is loaded. + """ + + def __init__(self, problem): + self.__problem = problem + self.__steps = [] # this stores a list of time steps + self.__results = {} # stores results, nested dicts + self.__time = -1 + if self.__problem.solved: + self.load() + + @property + def steps(self): + """Returns a list of loaded time steps. + + Note: this is read only, you can not assign a value to it. + """ + return self.__steps + + @property + def time(self): + """Returns the current time (float) in the results file. + + Note: this is read only, you can not assign a value to it. + """ + return self.__time + + @staticmethod + def __metric_num(number, sig_figs=3, sci=False): + """Returns string of number, with only 10**3 suffixes. + + If 0 <= number < 1000 no suffix is added. + This is useful for quantities usually given in metric: stress, displacement. + + Args: + number (float or int): the number we want converted to __metric_number + sig_figs (int): number of significant figures to use, right of decimal + sci (bool): True means use scientific formatting, False use metric + """ + if sci: + format_str = "%.{}e".format(sig_figs) + my_str = format_str % number + else: + format_str = "%.{}f".format(sig_figs) + my_str = format_str % number + if number != 0: + # get the scientific exponent + exp = math.floor(math.log10(abs(number))) + metric_exp = exp - (exp % 3) + new_float = number/(10**metric_exp) + if metric_exp != 0: + format_str = "%.{}fe%i".format(sig_figs) + my_str = format_str % (new_float, metric_exp) + return my_str + +
[docs] def load(self): + """Loads the results file with problem.fname prefix.""" + self.__read_frd() # read nodal results + self.__read_dat() # read element integration pt results
+ +
[docs] def nplot(self, field, fname='', display=True, levels=21, gradient=False, + gmult=1.0, max_val=None, min_val=None, title=''): + """Plots nodal results. + + Args: + field (str): results item to plot, examples: 'ux', 'ey', 'Seqv' + fname (str): prefix of png file name, if writing an image + display (bool): True = interactively show the plot + levels (int): number of levels to use in the colorbar + gradient (bool): True = results plotted with gradient + False = results plotted with filled areas + gmult (int): geometric multiplier on displacement of nodes + displayed_node_loc = model_node_loc + gmult*node_displacement + max_val (float or None): max value in the colorbar + + - None: max from selected data used + - float: use the passed float + min_val (float): min value in the colorbar + + - None: min from selected data used + - float: use the passed float + title (str): third line in the plot title + """ + # store the selected nodes and elements + sel = {} + sel['nodes'] = self.__problem.fea.view.nodes + sel['elements'] = self.__problem.fea.view.elements + sel['faces'] = self.__problem.fea.view.faces + + # sort nodes low to high so index is correct + # we have index to id below so showing subsets works + sel['nodes'] = list(sel['nodes']) + sel['nodes'] = sorted(sel['nodes'], key=lambda k: k.id) + + # store results at nodes + axials = [] + radials = [] + zvals = [] + id_to_ind = {} + for node in sel['nodes']: + id_to_ind[node.id] = len(axials) + axi = node.y + gmult*self.__results[self.__time]['node'][node.id]['uy'] + rad = node.x + gmult*self.__results[self.__time]['node'][node.id]['ux'] + axials.append(axi) + radials.append(rad) + zvals.append(self.__results[self.__time]['node'][node.id][field]) + + # make a list of triangles, given by indices, looping anticlockwise + triangles = [] + mylist = [] + if len(sel['elements']) > 0: + mylist = sel['elements'] + elif len(sel['faces']) > 0: + mylist = sel['faces'] + for element in mylist: + tris = element.get_tris() # list of triangle nodes + for tri in tris: + for ind, nid in enumerate(tri): + tri[ind] = id_to_ind[nid] # convert id to index + triangles += tris + + # check to see if selected nodes and elements are + # in the parent model's nodes and elements + fig = plt.figure() + ax_ = fig.add_subplot(111) + + # need to set tick list here + vmin = min(zvals) + vmax = max(zvals) + stop_plot = False + if max_val != None and min_val == None: + if max_val < vmin: + stop_plot = True + print('Error:') + print(' Only max was passed but it is < the data min!') + print(' Pass a max_val that is > the data min of %f' % vmin) + else: + vmax = max_val + elif min_val != None and max_val == None: + if min_val > vmax: + stop_plot = True + print('Error:') + print(' Only min was passed but it is > the data max!') + print(' Pass a min_val that is < the data max of %f' % vmax) + else: + vmin = min_val + elif max_val != None and min_val != None: + if max_val < min_val: + stop_plot = True + print('Error:') + print(' Min and max passed, but min > max!') + print(' Pass a min_val that is < max_val') + else: + vmax = max_val + vmin = min_val + # exit if stop plot flag is on + if stop_plot: + return None + + tick_list = [vmin] + if vmax != vmin: + # we have a range of values we're plotting + tick_list = linspace(vmin, vmax, levels+1) + + # plot using a gradient(shaded) or levels + # code required for the colorbar, needs to go before plotting for colormap + cnorm = colors.Normalize(vmin=vmin, vmax=vmax) + cmap = colors.ListedColormap(['b', 'b']) # default to plot one val + if vmax != vmin: + # we have a range of values we're plotting + if gradient: + cmap = plt.get_cmap(CMAP) + else: + cmap = plt.get_cmap('jet', levels) + cmap.set_under('0.3', 0.8) + cmap.set_over('0.7', 0.8) + if gradient or len(tick_list) == 1: + # This one is shaded + plt.tripcolor(axials, radials, triangles, zvals, shading='gouraud', + cmap=cmap, norm=cnorm) + else: + # this one is not shaded + plt.tricontourf(axials, radials, triangles, zvals, levels=tick_list, + cmap=cmap, norm=cnorm, extend='both') + + scalarmap = cmx.ScalarMappable(norm=cnorm, cmap=cmap) + scalarmap.set_array([]) + cbar = plt.colorbar(scalarmap, orientation='vertical', ticks=tick_list) + + scibool = False + if field[0] == 'e': + # strain plotting, use scientific numbering + scibool = True + met_max = self.__metric_num(max(zvals), sci=scibool) + met_min = self.__metric_num(min(zvals), sci=scibool) + label = 'Max: %s\nMin: %s' % (met_max, met_min) + tick_list = [self.__metric_num(tick, sci=scibool) for tick in tick_list] + cbar.ax.set_yticklabels(tick_list) + cbar.ax.set_xlabel(label, labelpad=10, x=0, ha='left') + cbar.ax.xaxis.set_label_position('top') + + # set the horizontal and vertical axes + base_classes.plot_set_bounds(plt, axials, radials) + + # set units + alist = self.__problem.fea.get_units(field, 'dist', 'time') + [f_unit, d_unit, t_unit] = alist + + # set plot axes + plot_title = ('Node %s%s\nTime=%f%s' % + (field, f_unit, self.__time, t_unit)) + if title != '': + plot_title += '\n%s' % title + plt.title(plot_title) + plt.xlabel('axial, y'+d_unit) + plt.ylabel('radial, x'+d_unit) + ax_.set_aspect('equal') + if gmult != 1: + ax_.xaxis.set_ticklabels([]) + ax_.yaxis.set_ticklabels([]) + base_classes.plot_finish(plt, fname, display)
+ +
[docs] def eplot(self, field, fname='', display=True, levels=21, + gmult=1.0, mode='avg', max_val=None, min_val=None, title=''): + """Plots element results. + + Args: + field (str): results item to plot. Only stresses supported. + Examples: 'Sx', 'Sxy', 'S1', 'Seqv' etc. + fname (str): prefix of png file name, if writing an image + display (bool): True = interactively show the plot + levels (int): number of levels to use in the colorbar + gmult (int): geometric multiplier on displacement of nodes + displayed_node_loc = model_node_loc + gmult*node_displacement + mode (str): the type of element result to plot + + - 'avg': integration points averaged to avg element result + - 'max': max value of field in the integration points plotted + - 'min': min value of field in the integration points plotted + max_val (float or None): max value in the colorbar + + - None: max from selected data used + - float: use the passed float + min_val (float): min value in the colorbar + + - None: min from selected data used + - float: use the passed float + title (str): third line in the plot title + """ + # store the selected nodes and elements + sel = {} + sel['nodes'] = self.__problem.fea.view.nodes + sel['elements'] = self.__problem.fea.view.elements + sel['faces'] = self.__problem.fea.view.faces + + # sort nodes low to high so index is correct + # we have index to id below so showing subsets works + sel['nodes'] = list(sel['nodes']) + sel['nodes'] = sorted(sel['nodes'], key=lambda k: k.id) + + # store results at nodes + axials = [] + radials = [] + zvals = [] + id_to_ind = {} + for node in sel['nodes']: + id_to_ind[node.id] = len(axials) + axi = node.y + gmult*self.__results[self.__time]['node'][node.id]['uy'] + rad = node.x + gmult*self.__results[self.__time]['node'][node.id]['ux'] + axials.append(axi) + radials.append(rad) + + # make a list of triangles, given by indices, looping anticlockwise + triangles = [] + mylist = [] + if len(sel['elements']) > 0: + mylist = sel['elements'] + elif len(sel['faces']) > 0: + mylist = sel['faces'] + for ele in mylist: + val = self.__results[self.__time]['element'][ele.id][mode][field] + tris = ele.get_tris() # list of triangle nodes defined by node id + for tri in tris: + zvals.append(val) + for ind, nid in enumerate(tri): + tri[ind] = id_to_ind[nid] # convert id to index + triangles += tris + + # check to see if selected nodes and elements are + # in the parent model's nodes and elements + + fig = plt.figure() + ax_ = fig.add_subplot(111) + + # need to set tick list here + vmin = min(zvals) + vmax = max(zvals) + stop_plot = False + if max_val != None and min_val == None: + if max_val < vmin: + stop_plot = True + print('Error:') + print(' Only max was passed but it is < the data min!') + print(' Pass a max_val that is > the data min of %f' % vmin) + else: + vmax = max_val + elif min_val != None and max_val == None: + if min_val > vmax: + stop_plot = True + print('Error:') + print(' Only min was passed but it is > the data max!') + print(' Pass a min_val that is < the data max of %f' % vmax) + else: + vmin = min_val + elif max_val != None and min_val != None: + if max_val < min_val: + stop_plot = True + print('Error:') + print(' Min and max passed, but min > max!') + print(' Pass a min_val that is < max_val') + else: + vmax = max_val + vmin = min_val + # exit if stop plot flag is on + if stop_plot: + return None + + tick_list = [vmin] + if vmax != vmin: + # we have a range of values we're plotting + tick_list = linspace(vmin, vmax, levels+1) + + # code required for the colorbar, needs to go before plotting for cmap + cnorm = colors.Normalize(vmin=vmin, vmax=vmax) + cmap = colors.ListedColormap(['b', 'b']) # default to plot one val + if vmax != vmin: + # we have a range of values we're plotting + cmap = plt.get_cmap(CMAP, levels) + cmap.set_under('0.3', 0.8) + cmap.set_over('0.7', 0.8) + + # plot using levels + plt.tripcolor(axials, radials, triangles, zvals, + shading='flat', cmap=cmap, norm=cnorm) + + scalarmap = cmx.ScalarMappable(norm=cnorm, cmap=cmap) + scalarmap.set_array([]) + cbar = plt.colorbar(scalarmap, orientation='vertical', ticks=tick_list) + scibool = False + if field[0] == 'e': + # strain plotting, use scientific numbering + scibool = True + met_max = self.__metric_num(max(zvals), sci=scibool) + met_min = self.__metric_num(min(zvals), sci=scibool) + label = 'Max: %s\nMin: %s' % (met_max, met_min) + tick_list = [self.__metric_num(tick, sci=scibool) for tick in tick_list] + cbar.ax.set_yticklabels(tick_list) + cbar.ax.set_xlabel(label, labelpad=10, x=0, ha='left') + cbar.ax.xaxis.set_label_position('top') + + # set the horizontal and vertical axes + base_classes.plot_set_bounds(plt, axials, radials) + + # set units + alist = self.__problem.fea.get_units(field, 'dist', 'time') + [f_unit, d_unit, t_unit] = alist + + # set plot axes + plot_title = ('Element %s %s%s\nTime=%f%s' % + (mode, field, f_unit, self.__time, t_unit)) + if title != '': + plot_title += '\n%s' % title + plt.title(plot_title) + plt.xlabel('axial, y'+d_unit) + plt.ylabel('radial, x'+d_unit) + ax_.set_aspect('equal') + if gmult != 1: + ax_.xaxis.set_ticklabels([]) + ax_.yaxis.set_ticklabels([]) + base_classes.plot_finish(plt, fname, display)
+ +
[docs] def set_time(self, time): + """Sets the time point we're looking at in the results file. + + Args: + time (float): time we are setting + """ + if time in self.steps: + self.__time = time + print('Results file time set to: %f' % (self.__time)) + else: + print('Time %f is not in the loaded times. Valid times are:') + print(self.steps)
+ + +
[docs] def plot_gradient(self, start_point, end_point, field, fname='', display=True, title='', max_val=None, min_val=None, curve_fitting=True, n_poly=3, n_subpoints=500, legend=True): + """Create diagram with data projected onto line on the undeformed geometry. + + Args: + start_point [float, float]: starting point of line. [x, y] + end_point [float, float]: end point of line. Example: [x, y] + field (str): results item to plot, examples: 'ux', 'ey', 'Seqv' + fname (str): prefix of png file name, if writing an image + display (bool): True = interactively show the plot + title (str): third line in the plot title + max_val (float or None): max value in the y-axis + - None: max from selected data used + - float: use the passed float + min_val (float or None): min value in the y-axis + - None: min from selected data used + - float: use the passed float + curve_fitting (bool): True = a curve is fitted to the gradient + n_poly (int): numbers of polygons for fitting + n_subpoints (int): numbers of points the line is subdivided into + legend (bool): True = legend with fitted equation is shown + """ + + # store the selected nodes and elements + sel = {} + sel['nodes'] = self.__problem.fea.view.nodes + + # sort nodes low to high so index is correct + # we have index to id below so showing subsets works + sel['nodes'] = list(sel['nodes']) + sel['nodes'] = sorted(sel['nodes'], key=lambda k: k.id) + + # store results at nodes + node_position = np.zeros((len(sel['nodes']),2)) + field_values = np.zeros(len(sel['nodes'])) + + for idx, node in enumerate(sel['nodes']): + + node_position[idx] = [node.x, node.y] + field_values[idx] = self.__results[self.__time]['node'][node.id][field] + + + #create subpoints on line + subpoints = np.zeros((n_subpoints, 3)) #[x, y, line position] + + subpoints[:,0] = np.linspace(start_point[0], end_point[0], n_subpoints) + subpoints[:,1] = np.linspace(start_point[1], end_point[1], n_subpoints) + subpoints[:,2] = np.arange(n_subpoints) / n_subpoints * np.sqrt(np.sum( (np.array(start_point) - np.array(end_point))**2)) + + #calculate weighted field value for every subpoint + wfield = np.zeros(n_subpoints) + + for idx in range(n_subpoints): + + #calculate inverse of distance from nodes to subpoints + dist = np.sqrt(np.sum((node_position-subpoints[idx,0:2])**2,axis=1)) + + #calculte weighted field value + #dist[dist < 1E-10] = 1E-10 + #inv_dist = 1. / dist**3 + #wfield[idx] = np.average(field_values, weights=inv_dist) + + #use nearest value + wfield[idx] = field_values[min(range(len(dist)),key=dist.__getitem__)] + + + #plot diagram + + fig = plt.figure(figsize=(10,6)) + ax_ = fig.add_subplot(111) + + plt.plot(subpoints[:,2], wfield, '-r', linewidth=2.5, label=field) + + if curve_fitting==True: + #execute curve fitting if needed + poly = np.polyfit(subpoints[:,2], wfield, n_poly) + + #string for equation of fitted function + funcstring = [str(np.round(poly[i]))+u'*x^'+str(np.arange(n_poly,0,-1)[i]) for i in range(n_poly)] + funcstring.append(str(np.round(poly[-1]))) + funcstring = '+'.join(funcstring) + + func = np.poly1d(poly) + + plt.plot(subpoints[:,2], func(subpoints[:,2]), '--k', linewidth=1.5, label=funcstring) + + + # set units + alist = self.__problem.fea.get_units(field, 'dist', 'time') + [f_unit, d_unit, t_unit] = alist + + # set plot axes + plot_title = ('Gradient %s%s\nTime=%f%s' %(field, f_unit, self.__time, t_unit)) + if title != '': + plot_title += '\n%s' % title + plt.title(plot_title) + plt.xlabel('path position'+d_unit) + plt.ylabel(field + ' ' +f_unit) + + #show legend if needed + if legend == True: + plt.legend() + + #set limits on y-axis + if min_val!=None: + plt.gca().set_ylim(bottom=min_val) + if max_val!=None: + plt.gca().set_ylim(top=max_val) + + plt.grid() + base_classes.plot_finish(plt, fname, display)
+ +
[docs] def get_relative_gradient(self, start_point, end_point, field, n_poly=3, n_subpoints=500): + """Calculte relative stress gradient (gradient/start_value) + + Args: + start_point [(float), (float)]: starting point of line. [x, y] + end_point [(float), (float)]: end point of line. Example: [x, y] + field (str): results item to plot, examples: 'ux', 'ey', 'Seqv' + + Kargs: + n_poly (int): numbers of polygons for fitting, min=2 + n_subpoints (int): numbers of points the line is subdivided into + """ + + # store the selected nodes and elements + sel = {} + sel['nodes'] = self.__problem.fea.view.nodes + + # sort nodes low to high so index is correct + # we have index to id below so showing subsets works + sel['nodes'] = list(sel['nodes']) + sel['nodes'] = sorted(sel['nodes'], key=lambda k: k.id) + + # store results at nodes + node_position = np.zeros((len(sel['nodes']),2)) + field_values = np.zeros(len(sel['nodes'])) + + for idx, node in enumerate(sel['nodes']): + + node_position[idx] = [node.x, node.y] + field_values[idx] = self.__results[self.__time]['node'][node.id][field] + + + #create subpoints on line + subpoints = np.zeros((n_subpoints, 3)) #[x, y, line position] + + subpoints[:,0] = np.linspace(start_point[0], end_point[0], n_subpoints) + subpoints[:,1] = np.linspace(start_point[1], end_point[1], n_subpoints) + subpoints[:,2] = np.arange(n_subpoints) / n_subpoints * np.sqrt(np.sum( (np.array(start_point) - np.array(end_point))**2)) + + #calculate weighted field value for every subpoint + wfield = np.zeros(n_subpoints) + + for idx in range(n_subpoints): + + #calculate inverse of distance from nodes to subpoints + dist = np.sqrt(np.sum((node_position-subpoints[idx,0:2])**2,axis=1)) + + #use nearest value + wfield[idx] = field_values[min(range(len(dist)),key=dist.__getitem__)] + + + #curve fitting + poly = np.polyfit(subpoints[:,2], wfield, n_poly) + + rel_grad = abs(poly[-2])/abs(poly[-1]) + + return rel_grad
+ + + + + + @staticmethod + def __utot(vals): + """Returns the total displacement distance, given [dx,dy,dz]. + + Args: + vals (list): [dx, dy, dz] list of displacements in x, y, and z axes + + Returns: + res (float): displacement + """ + # computes sum of the squares + res = [a**2 for a in vals] + res = (sum(res))**0.5 + return res + + @staticmethod + def __seqv(vals): + """Returns the Von Mises stress, which will be stored as 'Seqv'. + + Args: + vals (list): list of six stresses [s11,s22,s33,s12,s13,s23] + + Returns: + res (float): Von Mises stress + """ + [s11, s22, s33, s12, s13, s23] = vals + aval = s11 - s22 + bval = s22 - s33 + cval = s33 - s11 + dval = s12**2 + s23**2 +s13**2 + res = (0.5*(aval**2 + bval**2 + cval**2 +6*dval))**0.5 + return res + + @staticmethod + def __principals(vals): + """Returns principal stresses [S1,S2,S3]. + + Args: + vals (list): six stresses [s11,s22,s33,s12,s13,s23] + + Returns: + res (list): principal stresses [S1,S2,S3] stresses are high-to-low + """ + # calculates and returns principal stresses, S1, S2, S3 + [s11, s22, s33, s12, s13, s23] = vals + aval = 1 + bval = (s11 + s22 + s33)*-1.0 + cval = (s11*s22 + s11*s33 + s22*s33 - s12**2 - s13**2 - s23**2) + dval = (s11*s22*s33 + 2*s12*s13*s23 - s11*(s23**2) - s22*(s13**2) + - s33*(s12**2))*-1.0 + res = list(roots([aval, bval, cval, dval])) + res = sorted(res, reverse=True) + return res + + def __get_data_dict(self, time, type_str): + """Returns the data dict at the correct time for element or node. + + Args: + time (float): None or the time we want, if None use current time + type_str: 'element' or 'node' + + Returns: + res (dict or None): dictionary with field values in it + None if the time was invalid + """ + res = self.__results[self.__time][type_str] + if time != None: + if time not in self.steps: + print('Error: passed time is not in steps!') + print(' Pass a time in the steps:') + print(self.steps) + return None + else: + res = self.__results[time][type_str] + return res + +
[docs] def get_nmax(self, field, time=None): + """Returns the max value of node results field in selected nodes. + + Reports results for the current time. + + Args: + field (str): results field, for example 'ux', 'ey', 'S1', 'Seqv' + time (None or float): the time to query + + - None: uses the current time + - float: uses the passed float time + Returns: + res (float): max value + """ + nodes = self.__problem.fea.view.nodes + node_ids = [node.id for node in nodes] + data_dict = self.__get_data_dict(time, 'node') + if data_dict == None: + return None + ndicts = [data_dict[nid] for nid in node_ids] + res = [ndict[field] for ndict in ndicts] + res = max(res) + return res
+ +
[docs] def get_nmin(self, field, time=None): + """Returns the min value of node results field in selected nodes. + + Reports results for the current time. + + Args: + field (str): results field, for example 'ux', 'ey', 'S1', 'Seqv' + time (None or float): the time to query + + - None: uses the current time + - float: uses the passed float time + Returns: + res (float): min value + """ + nodes = self.__problem.fea.view.nodes + node_ids = [node.id for node in nodes] + data_dict = self.__get_data_dict(time, 'node') + if data_dict == None: + return None + ndicts = [data_dict[nid] for nid in node_ids] + res = [ndict[field] for ndict in ndicts] + res = min(res) + return res
+ +
[docs] def get_nval(self, node, field, time=None): + """Returns the field result value under node. + + Result will be returned whether or not passed node is selected. + + Args: + node (str or Node): node we are asking about + field (str): the results item we want: 'ux', 'Sy', 'Seqv', 'fx' + time (None or float): the time to query + + - None: uses the current time + - float: uses the passed float time + Returns: + res (float or None): float value if field exists, None otherwise + """ + items = self.__problem.fea.get_item(node) + if len(items) == 1: + if isinstance(items[0], mesh.Node): + nnum = items[0].id + data_dict = self.__get_data_dict(time, 'node') + if data_dict == None: + return None + ndict = data_dict[nnum] + if field in ndict: + res = ndict[field] + return res + else: + print('Passed field is not in the results!') + return None + else: + print('You did not pass in a node!') + print('A single node or string node name must be passed in!') + return None + else: + print('A single node or string node name must be passed in!') + return None
+ +
[docs] def get_fsum(self, item): + """Returns the force sum on nodes under a given point or line. + + Reports results for the current time. + + Args: + item (Point or SignLine): item that has reaction forces on its nodes + + Returns: + list: [fx, fy, fz] reaction forces in each axis, force units + """ + (fxx, fyy, fzz) = ([], [], []) + nodes = item.nodes + nodes = [n.id for n in nodes] + for node in nodes: + f_x = self.__results[self.__time]['node'][node]['fx'] + f_y = self.__results[self.__time]['node'][node]['fy'] + f_z = self.__results[self.__time]['node'][node]['fz'] + if f_x != 0 or f_y != 0 or f_z != 0: + fxx.append(f_x) + fyy.append(f_y) + fzz.append(f_z) + fxx = sum(fxx) + fyy = sum(fyy) + fzz = sum(fzz) + return [fxx, fyy, fzz]
+ +
[docs] def get_emax(self, field, time=None, mode='avg'): + """Returns the max results field value of selected elements at curent time. + + Args: + field (str): results field, stresses supported 'S1', 'Sx', etc. + time (None or float): the time to query + + - None: uses the current time + - float: uses the passed float time + mode (str): type of element result to give back + + - 'max': for each element only use the max value of field over + all of its integration points + - 'min': for each element only use the min value of field over + all of its integration points + - 'avg': for each element only use an average of all integration + points in the eleemnt. Principal streses and Seqv are + calculated after averaging 6 stress components. + Returns: + res (float): max value + """ + res = [] + elements = self.__problem.fea.view.elements + data_dict = self.__get_data_dict(time, 'element') + if data_dict == None: + return None + for element in elements: + enum = element.id + edict = data_dict[enum][mode] + res.append(edict[field]) + res = max(res) + return res
+ +
[docs] def get_emin(self, field, time=None, mode='avg'): + """Returns the min results field value of selected elements at curent time. + + Args: + field (str): results field, stresses supported 'S1', 'Sx', etc. + time (None or float): the time to query + + - None: uses the current time + - float: uses the passed float time + mode (str): type of element result to give back + + - 'max': for each element only use the max value of field over + all of its integration points + - 'min': for each element only use the min value of field over + all of its integration points + - 'avg': for each element only use an average of all integration + points in the eleemnt. Principal streses and Seqv are + calculated after averaging 6 stress components. + Returns: + res (float): min value + """ + res = [] + elements = self.__problem.fea.view.elements + data_dict = self.__get_data_dict(time, 'element') + if data_dict == None: + return None + for element in elements: + enum = element.id + edict = data_dict[enum][mode] + res.append(edict[field]) + res = min(res) + return res
+ +
[docs] def get_eval(self, element, field, time=None, mode='avg'): + """Returns the field result value under element. + + Result will be returned whether or not passed element is selected. + + Args: + element (str or Element): element we are asking about + field (str): the results item we want: 'Sy', 'Seqv' + mode (str): the type of element result to get + + - 'avg': integration points averaged to avg element result + - 'max': max value of field in the integration points plotted + - 'min': min value of field in the integration points plotted + Returns: + res (float or None): float value if field exists, None otherwise + """ + items = self.__problem.fea.get_item(element) + if len(items) == 1: + if isinstance(items[0], mesh.Element): + enum = items[0].id + data_dict = self.__get_data_dict(time, 'element') + if data_dict == None: + return None + edict = data_dict[enum][mode] + if field in edict: + res = edict[field] + return res + else: + print('Passed field is not in the results!') + return None + else: + print('You did not pass in a element!') + print('A single element or string element name must be given!') + return None + else: + print('A single element or string element name must be given!') + return None
+ + @staticmethod + def __get_vals(fstr, line): + """Returns a list of typed items based on an input format string. + + Args: + fst (str): C format string, commas separate fields + line (str): line string to parse + + Returns: + res (list): list of typed items extracted from the line + """ + res = [] + fstr = fstr.split(',') + thestr = str(line) + for item in fstr: + if item[0] == "'": + # strip off the char quaotes + item = item[1:-1] + # this is a string entry, grab the val out of the line + ind = len(item) + fwd = thestr[:ind] + thestr = thestr[ind:] + res.append(fwd) + else: + # format is: 1X, A66, 5E12.5, I12 + # 1X is number of spaces + (mult, ctype) = (1, None) + m_pat = re.compile(r'^\d+') # find multiplier + c_pat = re.compile(r'[XIEA]') # find character + if m_pat.findall(item) != []: + mult = int(m_pat.findall(item)[0]) + ctype = c_pat.findall(item)[0] + if ctype == 'X': + # we are dealing with spaces, just reduce the line size + thestr = thestr[mult:] + elif ctype == 'A': + # character string only, add it to results + fwd = thestr[:mult].strip() + thestr = thestr[mult:] + res.append(fwd) + else: + # IE, split line into m pieces + w_pat = re.compile(r'[IE](\d+)') # find the num after char + width = int(w_pat.findall(item)[0]) + while mult > 0: + # only add items if we have enough line to look at + if width <= len(thestr): + substr = thestr[:width] + thestr = thestr[width:] + substr = substr.strip() # remove space padding + if ctype == 'I': + substr = int(substr) + elif ctype == 'E': + substr = float(substr) + res.append(substr) + mult -= 1 + return res + + @staticmethod + def __get_first_dataline(infile): + """ + Reads infile until a line with data is found, then returns it + A line that starts with ' -1' has data + """ + while True: + line = infile.readline() + if line[:3] == ' -1': + return line + + def __store_time(self, time): + """Stores the passed time in the results steps""" + if time not in self.__steps: + self.__steps.append(time) + if time not in self.__results: + new_dict = {'node': collections.defaultdict(dict), + 'element': collections.defaultdict(dict)} + self.__results[time] = new_dict + + def __modearr_estrsresults(self, infile, line): + """Returns an array of line, mode, rfstr, time""" + words = line.strip().split() + # add time if not present + time = float(words[-1]) + self.__store_time(time) + # set mode + rfstr = "I10,2X,I2,6E14.2" + mode = 'stress' + infile.readline() + line = infile.readline() + return [line, mode, rfstr, time] + + def __modearr_nresults(self, infile): + """Returns an array of line, mode, rfstr, time""" + line = infile.readline() + fstr = "1X,' 100','C',6A1,E12.5,I12,20A1,I2,I5,10A1,I2" + tmp = self.__get_vals(fstr, line) + #[key, code, setname, value, numnod, text, ictype, numstp, analys, format_] + time, format_ = tmp[3], tmp[9] + + # set results format to short, long or binary + # only short and long are parsed so far + if format_ == 0: + rfstr = "1X,I2,I5,6E12.5" + elif format_ == 1: + rfstr = "1X,I2,I10,6E12.5" + elif format_ == 2: + # binary + pass + + # set the time + self.__store_time(time) + + # get the name to determine if stress or displ + line = infile.readline() + fstr = "1X,I2,2X,8A1,2I5" + # [key, name, ncomps, irtype] + name = self.__get_vals(fstr, line)[1] + + mode_by_name = {'DISP': 'displ', + 'STRESS': 'stress', + 'TOSTRAIN': 'strain', + 'FORC': 'force'} + mode = mode_by_name[name] + print('Reading '+mode+' storing: '+ + ','.join(base_classes.RESFIELDS[mode])) + + line = self.__get_first_dataline(infile) + return [line, mode, rfstr, time] + + def _save_node_displ(self, line, rfstr, time, mode='displ'): + """Saves node displacement""" + node, ux_, uy_, uz_ = self.__get_vals(rfstr, line)[1:] + labs = base_classes.RESFIELDS[mode] + vals = [ux_, uy_, uz_] + utot = self.__utot(vals) + vals.append(utot) + adict = self.__results[time]['node'][node] + for (label, val) in zip(labs, vals): + adict[label] = val + + def _save_node_stress(self, line, rfstr, time, mode='stress'): + """Saves node stress""" + tmp = self.__get_vals(rfstr, line) + # [key, node, sx, sy, sz, sxy, syz, szx] + node, sxx, syy, szz, sxy, syz, szx = tmp[1:] + labs = base_classes.RESFIELDS[mode] + vals = [sxx, syy, szz, sxy, syz, szx] + seqv = self.__seqv(vals) + s_1, s_2, s_3 = self.__principals(vals) + vals.append(seqv) + vals += [s_1, s_2, s_3] + adict = self.__results[time]['node'][node] + for (label, val) in zip(labs, vals): + adict[label] = val + + def _save_node_strain(self, line, rfstr, time, mode='strain'): + """Saves node strain""" + tmp = self.__get_vals(rfstr, line) + # [key, node, ex, ey, ez, exy, eyz, ezx] + node, exx, eyy, ezz, exy, eyz, ezx = tmp[1:] + labs = base_classes.RESFIELDS[mode] + vals = [exx, eyy, ezz, exy, eyz, ezx] + eeqv = self.__seqv(vals) + e_1, e_2, e_3 = self.__principals(vals) + vals.append(eeqv) + vals += [e_1, e_2, e_3] + adict = self.__results[time]['node'][node] + for (label, val) in zip(labs, vals): + adict[label] = val + + def _save_node_force(self, line, rfstr, time, mode='force'): + """Saves node force""" + # [key, node, fx, fy, fz] + node, f_x, f_y, f_z = self.__get_vals(rfstr, line)[1:] + labs = base_classes.RESFIELDS[mode] + vals = [f_x, f_y, f_z] + adict = self.__results[time]['node'][node] + for (label, val) in zip(labs, vals): + adict[label] = val + + def _save_ele_stress(self, line, rfstr, time, + mode='stress'): + """Saves element integration point stresses""" + labels = ['Sx', 'Sy', 'Sz', 'Sxy', 'Sxz', 'Syz'] + vals = self.__get_vals(rfstr, line) + # element_number, integration_pt_number + enum, ipnum = vals[0], vals[1] + stress_vals = vals[2:] + + adict = {} + for (label, val) in zip(labels, stress_vals): + adict[label] = val + if enum not in self.__results[time]['element']: + start_val = {'ipoints': {}, + 'avg': {}, + 'min': {}, + 'max': {}} + self.__results[time]['element'][enum] = start_val + # each line is an integration point result + self.__results[time]['element'][enum]['ipoints'][ipnum] = adict + +
[docs] def check_ccx_version(self, timeout=1): + """Raises an exception of the calculix ccx version is too old""" + runstr = "%s -version" % (environment.CCX) + try: + output_str = subprocess.check_output(runstr, + timeout=timeout, + shell=True) + except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as ex: + output_str = ex.output + output_str = str(output_str, 'utf-8') + matches = re.findall(r'\d+\.\d+', output_str) + version_number = matches[-1] + print('Using Calculix ccx version=%s ' + '(trailing characters like the p in 2.8p are omitted)' + % version_number) + major_version, minor_version = [int(f) for f + in version_number.split('.')] + if major_version <= 2 and minor_version <= 8: + raise Exception('Your version of calculix ccx is too old! ' + 'Please update it to version >=2.8 with ' + 'the command:\npycalculix-add-feaprograms') + version = float(output_str.strip().split()[-1])
+ # extract version with regex + + def __read_frd(self): + """ + Reads a ccx frd results file which contains nodal results. + The file format is desribed in the cgx docs + """ + fname = self.__problem.fname+'.frd' + if not os.path.isfile(fname): + print("Error: %s file not found" % fname) + return + # frd reading uses formatting from ccx 2.8 or higher so + # throw an exception if our version is too old + self.check_ccx_version(timeout=1) + infile = open(fname, 'r') + print('Loading nodal results from file: '+fname) + mode = None + time = 0.0 + rfstr = '' + while True: + line = infile.readline() + if not line: + break + + # set the results mode + if '1PSTEP' in line: + # we are in a results block + arr = self.__modearr_nresults(infile) + line, mode, rfstr, time = arr + + # set mode to none if we hit the end of a resuls block + if line[:3] == ' -3': + mode = None + if not mode: + continue + + node_data_saver = getattr(self, '_save_node_' + mode) + node_data_saver(line, rfstr, time) + + infile.close() + print('The following times have been read:') + print(self.__steps) + print('Nodal results from file: %s have been read.' % fname) + self.set_time(self.__steps[0]) + + def __read_dat(self): + """ + Reads ccx dat results file. + It has element integration point results. + """ + fname = self.__problem.fname+'.dat' + if not os.path.isfile(fname): + print('Error: %s file not found' % fname) + return + infile = open(fname, 'r') + print('Loading element results from file: '+fname) + mode = None + rfstr = '' + time = 0.0 + while True: + line = infile.readline() + if not line: + break + + # check for stress, we skip down to the line data when + # we call __modearr_estrsresults + if 'stress' in line: + arr = self.__modearr_estrsresults(infile, line) + line, mode, rfstr, time = arr + + # reset the read type if we hit a blank line + if line.strip() == '': + mode = None + if not mode: + continue + + # store stress results + self._save_ele_stress(line, rfstr, time) + + infile.close() + + # loop over all element results, calculating avg element result + # by averaging integration point vals + for time in self.__steps: + for edict in self.__results[time]['element'].values(): + ipoints = edict['ipoints'].values() + stress_types = ['Sx', 'Sy', 'Sz', 'Sxy', 'Sxz', 'Syz'] + strslist_by_strstype = collections.defaultdict(list) + # set stress values in max, min, avg locations + # of non-summary stress components + for stress_type in stress_types: + stress_vals = [ipt[stress_type] for ipt in ipoints] + stress_avg = sum(stress_vals)/len(stress_vals) + stress_max = max(stress_vals) + stress_min = min(stress_vals) + edict['avg'][stress_type] = stress_avg + edict['max'][stress_type] = stress_max + edict['min'][stress_type] = stress_min + strslist_by_strstype[stress_type] = stress_vals + # for each element, calc Seqv, S1, S2, S3 + # at each integration point + for ipt in ipoints: + stress_vals = [ipt[stress_type] for stress_type + in stress_types] + seqv = self.__seqv(stress_vals) + [s_1, s_2, s_3] = self.__principals(stress_vals) + strslist_by_strstype['Seqv'].append(seqv) + strslist_by_strstype['S1'].append(s_1) + strslist_by_strstype['S2'].append(s_2) + strslist_by_strstype['S3'].append(s_3) + # now at the element level, store the avg, max, and min + # of the Seqv and principal stresses we found at + # integration points + for stress_type in ['Seqv', 'S1', 'S2', 'S3']: + stress_vals = strslist_by_strstype[stress_type] + stress_avg = sum(stress_vals)/len(stress_vals) + stress_max = max(stress_vals) + stress_min = min(stress_vals) + edict['avg'][stress_type] = stress_avg + edict['max'][stress_type] = stress_max + edict['min'][stress_type] = stress_min + + + print('The following times have been read:') + print(self.__steps) + print('Element results from file: %s have been read.' % fname)
+
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_modules/pycalculix/selector.html b/dist/documentation/_modules/pycalculix/selector.html new file mode 100644 index 0000000..c291fc4 --- /dev/null +++ b/dist/documentation/_modules/pycalculix/selector.html @@ -0,0 +1,653 @@ + + + + + + + pycalculix.selector — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for pycalculix.selector

+"""This module stores the Selector object
+
+Selector is used in FeaModel to store the model's selected set.
+"""
+
+from . import partmodule
+from . import geometry
+from . import mesh
+from . import components
+
+
[docs]class Selector(object): + """Makes a selector which stores the selected items in the FeaModel. + + Args: + feamodel (FeaModel): the parent FeaModel object + + Attributes: + __fea (FeaModel): the parent FeaModel + __all (bool): If everything is selected all=True + parts (set): currently selected parts + areas (set): currently selected areas + lines (set): currently selected signed lines or arcs + points (set): currently selected points + elements (set): currently selected elements + faces (set): currently selected faces + nodes (set): currently selected nodes + __parts (set): internal storage set of parts + __areas (set): internal storage set of areas + __slines (set): internal storage set of signed lines and arcs + __points (set): internal storage set of points + __elements (set): internal storage set of elements + __faces (set): internal storage set of faces + __nodes (set): internal storage set of nodes + """ + def __init__(self, feamodel): + self.__fea = feamodel + self.__all = True + self.__parts = set() + self.__areas = set() + self.__slines = set() + self.__points = set() + self.__elements = set() + self.__faces = set() + self.__nodes = set() + + @property + def parts(self): + """Returns selected parts.""" + if self.__all: + return self.__fea.parts + else: + return self.__parts + + @property + def areas(self): + """Returns selected areas.""" + if self.__all: + return self.__fea.areas + else: + return self.__areas + + @property + def lines(self): + """Returns selected signed lines or arcs.""" + if self.__all: + return self.__fea.signlines + else: + return self.__slines + + @property + def points(self): + """Returns selected points.""" + if self.__all: + points = [pnt for pnt in self.__fea.points if pnt.arc_center == False] + return points + else: + return self.__points + + @property + def elements(self): + """Returns selected elements.""" + if self.__all: + return self.__fea.elements + else: + return self.__elements + + @property + def faces(self): + """Returns selected faces.""" + if self.__all: + return self.__fea.faces + else: + return self.__faces + + @property + def nodes(self): + """Returns selected nodes.""" + if self.__all: + return self.__fea.nodes + else: + return self.__nodes + + @staticmethod + def __add_items(inclusive, items, item, parent_list, this_set): + """Adds an item to items if appropriate. + + Helper function for select_above_all + """ + if inclusive == False: + items.add(item) + else: + parent_set = set(parent_list) + if parent_set.issubset(this_set): + items.add(item) + + def __add_select(self, items): + """Adds items to the selection set.""" + for item in items: + if isinstance(item, partmodule.Part): + self.__parts.add(item) + elif isinstance(item, geometry.Area): + self.__areas.add(item) + elif (isinstance(item, geometry.SignLine) + or isinstance(item, geometry.SignArc)): + self.__slines.add(item) + elif isinstance(item, geometry.Point): + self.__points.add(item) + elif isinstance(item, mesh.Element): + self.__elements.add(item) + elif isinstance(item, mesh.Face): + self.__faces.add(item) + elif isinstance(item, mesh.Node): + self.__nodes.add(item) + +
[docs] def allsel_under(self, sel_type, full=True, byfaces=False): + """Selects all progeny under current sel_type items. + + Args: + sel_type (str): the item type to select under + + - 'parts' + - 'areas' + - 'lines' + - 'points' (needs full=True) + - 'elements' + - 'faces' + + full (bool): if False selects under one branch + + - solid modeling branch: area->line->keypoints->points + - mesh branch: elements->faces->nodes + + Cross branch connections: + - faces are under lines + - elements are under areas + - nodes are under lines and points + + If True, selects all items connected to sub items: + - Parts: + + - Areas + - Elements, element faces + element nodes + - Lines, line points + + - Areas: + + - Elements, element faces + element nodes + - Lines, line points + + -Lines: + + - Faces, nodes + - byfaces=True: elements above faces + - byfaces=False: elements above line nodes selected + + -Points: + + - Nodes + - byfaces=True: no elements selected + - byfaces=False: elements above point nodes selected + + byfaces (bool): If true, the child elements will only be selected + if a parent face was selected. If false just an element node + needs to be selected to select the element. + + -This is only applicable when sel_type == 'lines' + """ + sets = ['parts', 'areas', 'lines', 'points', 'elements', 'faces'] + if isinstance(sel_type, str): + if sel_type in sets: + this_set = getattr(self, sel_type) + for item in this_set: + if isinstance(item, partmodule.Part): + # areas + self.select_below_all('parts') + # slines + self.select_below_all('areas') + # points + self.select_below_all('lines') + # select related elements if full is on + if full: + elements = item.elements + self.select(elements, also=True) + # sel faces + self.select_below_all('elements') + # sel nodes + self.select_below_all('faces') + + elif isinstance(item, geometry.Area): + # slines + self.select_below_all('areas') + # points + self.select_below_all('lines') + # select related elements if full is on + if full: + elements = item.elements + self.select(elements, also=True) + # sel faces + self.select_below_all('elements') + # sel nodes + self.select_below_all('faces') + elif (isinstance(item, geometry.SignLine) + or isinstance(item, geometry.SignArc)): + # sel points + self.select_below_all('lines') + # select related faces, nodes, and elements + if full: + # select faces + faces = item.faces + self.select(faces, also=True) + # sel nodes + self.select_below_all('faces') + # byfaces logic + if byfaces: + # sel elements above faces + self.select_above_all('faces', inclusive=False) + else: + # select elements connected to nodes + elements = set() + for node in self.nodes: + elements.update(node.elements) + self.select(elements, also=True) + elif isinstance(item, geometry.Point): + if full: + # sel nodes and elements + self.select(item.nodes, also=True) + if byfaces == False: + for node in item.nodes: + self.select(node.elements, also=True) + else: + print('The passed sel_type must be a valid type.') + else: + print('The passed sel_type must be a string!')
+ +
[docs] def select_below(self): + """Selects children below currently selected items. + + Only one set must be selected. + """ + sets = ['parts', 'areas', 'lines', 'points', 'elements', 'faces', + 'nodes'] + sel_sets = [] + for set_name in sets: + this_set = getattr(self, set_name) + if len(this_set) > 0: + sel_sets.append(set_name) + # check if number of visible sets is one + if len(sel_sets) == 0 or len(sel_sets) > 1: + print('Only one set must be selected to use this function.') + elif len(sel_sets) == 1: + sel_type = sel_sets[0] + if sel_type == 'nodes': + print("One can't select anything below nodes!'") + else: + self.select_below_all(sel_type)
+ +
[docs] def select_below_all(self, sel_type): + """Selects children below all currently selected items of sel_type. + + Args: + sel_type (str): type to select items below + + - 'parts': selects areas + - 'areas': selects lines (type SignLine and SignArc) + - 'lines': selects points + - 'elements': selects faces + - 'faces': selects nodes + """ + items = set() + if sel_type == 'parts': + for part in self.parts: + for area in part.areas: + items.add(area) + elif sel_type == 'areas': + for area in self.areas: + items.update(area.signlines) + elif sel_type == 'lines': + for sline in self.lines: + items.update(sline.line.points) + elif sel_type == 'elements': + for element in self.elements: + items.update(element.faces) + elif sel_type == 'faces': + for face in self.faces: + items.update(face.nodes) + self.__add_select(items)
+ +
[docs] def select_above(self, inclusive=False): + """Selects parents above currently selected items. + + Only one set must be selected. + """ + sets = ['parts', 'areas', 'lines', 'points', 'elements', 'faces', + 'nodes'] + sel_sets = [] + for set_name in sets: + this_set = getattr(self, set_name) + if len(this_set) > 0: + sel_sets.append(set_name) + # check if number of visible sets is one + if len(sel_sets) == 0 or len(sel_sets) > 1: + print('Only one set must be selected to use this function.') + elif len(sel_sets) == 1: + sel_type = sel_sets[0] + if sel_type == 'parts': + print("One can't select anything above parts!'") + else: + self.select_above_all(sel_type, inclusive)
+ +
[docs] def select_above_all(self, sel_type, inclusive=False): + """Selects parents above all currently selected items of sel_type. + + Args: + sel_type (str): type to select items above + + - 'areas': selects parts + - 'lines': selects areas + - 'points': selects signlines or signarcs + - 'faces': selects elements + - 'nodes': selects faces + + inclusive (bool): if True, all child entities must be selected for + the parent to be selected. If false all parents connected to + the child are selected. Exampes: + + - 'areas' + True: all part areas must be selected to select part + - 'areas' + False: parts with selected areas in them are selected + - 'lines' + True: all lines must be selected to select area + - 'lines' + False: areas with selected lines in them are selected + """ + items = set() + if sel_type == 'areas': + this_set = self.areas + for area in this_set: + item = area.parent + parent_list = area.parent.areas + self.__add_items(inclusive, items, item, parent_list, this_set) + elif sel_type == 'lines': + this_set = self.lines + for sline in this_set: + item = sline.parent + parent_list = sline.parent.signlines + self.__add_items(inclusive, items, item, parent_list, this_set) + elif sel_type == 'points': + this_set = set(self.points) + for point in this_set: + for parent_line in point.lines: + for sline in parent_line.signlines: + item = sline + parent_list = sline.points + self.__add_items(inclusive, items, item, parent_list, + this_set) + elif sel_type == 'faces': + this_set = set(self.faces) + for face in this_set: + item = face.element + parent_list = item.faces + self.__add_items(inclusive, items, item, parent_list, this_set) + elif sel_type == 'nodes': + this_set = set(self.nodes) + for node in this_set: + for face in node.faces: + item = face + parent_list = face.nodes + self.__add_items(inclusive, items, item, parent_list, + this_set) + self.__add_select(items)
+ +
[docs] def allselect(self): + """Selects all items.""" + self.select(items='all')
+ +
[docs] def select(self, items='', also=False): + """Selects an item or a list of items. + + Agrs: + items (str or item or list): item(s) to select + also (bool): whether to add the items to the current selection set + + - True: add the items to the current selection set + - False: only select the passed items + """ + # empty the selection set if also == False + if items == '': + print('You must pass in an item or item(s)!') + else: + if also == False: + self.select_none() + # Turn on 'all' mode if items == None, otherwise, get the items + if items == 'all': + self.__all = True + else: + items = self.__fea.get_items(items) + for (ind, item) in enumerate(items): + # if a component was passed, get its child items instead + if isinstance(item, components.Component): + real_items = item.get_children() + items[ind] = real_items + self.__add_select(items)
+ +
[docs] def select_all(self, sel_type='all', also=False): + """Selects all items of sel_type from the full feamodel. + + All items currently selected items are removed from the selection set + before the all set is selected. + + Args: + sel_type (str): the items to select + + - 'all' + - 'parts' + - 'areas' + - 'lines' + - 'points' + - 'elements' + - 'faces' + - 'nodes' + also (bool): if False, empties the selection set before selecting + all items, if True adds new items to the current set + """ + if also == False: + self.__all = False + self.select_none() + # now select all items of type + if sel_type == 'all': + self.select() + elif sel_type == 'parts': + self.__add_select(self.__fea.parts) + elif sel_type == 'areas': + self.__add_select(self.__fea.areas) + elif sel_type == 'lines': + self.__add_select(self.__fea.signlines) + elif sel_type == 'points': + points = [pnt for pnt in self.__fea.points if pnt.arc_center == False] + self.__add_select(points) + elif sel_type == 'elements': + self.__add_select(self.__fea.elements) + elif sel_type == 'faces': + self.__add_select(self.__fea.faces) + elif sel_type == 'nodes': + self.__add_select(self.__fea.nodes)
+ +
[docs] def deselect(self, items): + """Removes the passed item or items from the selection set. + """ + self.__all = False + for item in items: + if isinstance(item, partmodule.Part): + self.__parts.discard(item) + elif isinstance(item, geometry.Area): + self.__areas.discard(item) + elif (isinstance(item, geometry.SignLine) + or isinstance(item, geometry.SignArc)): + self.__slines.discard(item) + elif isinstance(item, geometry.Point): + self.__points.discard(item) + elif isinstance(item, mesh.Element): + self.__elements.discard(item) + elif isinstance(item, mesh.Face): + self.__faces.discard(item) + elif isinstance(item, mesh.Node): + self.__nodes.discard(item)
+ +
[docs] def deselect_all(self, sel_type): + """Deselects all items of sel_type. + + All items currently selected items are removed from the selection set + before the all set is selected. + + Args: + sel_type (str): the items to select + + - 'all' + - 'parts' + - 'areas' + - 'lines' + - 'points' + - 'elements' + - 'faces' + - 'nodes' + """ + if self.__all == True: + self.__parts = self.parts + self.__areas = self.areas + self.__slines = self.lines + self.__points = self.points + self.__elements = self.elements + self.__faces = self.faces + self.__nodes = self.nodes + self.__all = False + if sel_type == 'all': + self.select_none() + elif sel_type == 'parts': + self.__parts = set() + elif sel_type == 'areas': + self.__areas = set() + elif sel_type == 'lines': + self.__slines = set() + elif sel_type == 'points': + self.__points = set() + elif sel_type == 'elements': + self.__elements = set() + elif sel_type == 'faces': + self.__faces = set() + elif sel_type == 'nodes': + self.__nodes = set()
+ +
[docs] def select_none(self): + """Empties out the selection object. + """ + self.__all = False + self.__parts = set() + self.__areas = set() + self.__slines = set() + self.__points = set() + self.__elements = set() + self.__faces = set() + self.__nodes = set()
+ +
[docs] def print_summary(self): + """Prints a summary of the number so items selected in each sel_type. + """ + sets = ['parts', 'areas', 'lines', 'points', 'elements', 'faces', + 'nodes'] + spacer = '----------------------------------' + print(spacer) + print("View's currently selected items:") + for sel_type in sets: + items = getattr(self, sel_type) + print(' %s: %i selected' % (sel_type, len(items))) + print(spacer)
+ +
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/_sources/index.rst.txt b/dist/documentation/_sources/index.rst.txt new file mode 100644 index 0000000..a21a963 --- /dev/null +++ b/dist/documentation/_sources/index.rst.txt @@ -0,0 +1,21 @@ +.. pycalculix documentation master file, created by + sphinx-quickstart on Sat Nov 30 13:33:30 2019. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to pycalculix's documentation! +====================================== + +.. toctree:: + :maxdepth: 4 + :caption: Contents: + + pycalculix + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/dist/documentation/_sources/pycalculix.rst.txt b/dist/documentation/_sources/pycalculix.rst.txt new file mode 100644 index 0000000..74ad3c5 --- /dev/null +++ b/dist/documentation/_sources/pycalculix.rst.txt @@ -0,0 +1,142 @@ +pycalculix package +================== + +Submodules +---------- + +pycalculix.base\_classes module +------------------------------- + +.. automodule:: pycalculix.base_classes + :members: + :undoc-members: + :show-inheritance: + +pycalculix.cadimporter module +----------------------------- + +.. automodule:: pycalculix.cadimporter + :members: + :undoc-members: + :show-inheritance: + +pycalculix.components module +---------------------------- + +.. automodule:: pycalculix.components + :members: + :undoc-members: + :show-inheritance: + +pycalculix.connectors module +---------------------------- + +.. automodule:: pycalculix.connectors + :members: + :undoc-members: + :show-inheritance: + +pycalculix.environment module +----------------------------- + +.. automodule:: pycalculix.environment + :members: + :undoc-members: + :show-inheritance: + +pycalculix.feamodel module +-------------------------- + +.. automodule:: pycalculix.feamodel + :members: + :undoc-members: + :show-inheritance: + +pycalculix.geometry module +-------------------------- + +.. automodule:: pycalculix.geometry + :members: + :undoc-members: + :show-inheritance: + +pycalculix.installer module +--------------------------- + +.. automodule:: pycalculix.installer + :members: + :undoc-members: + :show-inheritance: + +pycalculix.loads module +----------------------- + +.. automodule:: pycalculix.loads + :members: + :undoc-members: + :show-inheritance: + +pycalculix.material module +-------------------------- + +.. automodule:: pycalculix.material + :members: + :undoc-members: + :show-inheritance: + +pycalculix.mesh module +---------------------- + +.. automodule:: pycalculix.mesh + :members: + :undoc-members: + :show-inheritance: + +pycalculix.partmodule module +---------------------------- + +.. automodule:: pycalculix.partmodule + :members: + :undoc-members: + :show-inheritance: + +pycalculix.problem module +------------------------- + +.. automodule:: pycalculix.problem + :members: + :undoc-members: + :show-inheritance: + +pycalculix.results\_file module +------------------------------- + +.. automodule:: pycalculix.results_file + :members: + :undoc-members: + :show-inheritance: + +pycalculix.selector module +-------------------------- + +.. automodule:: pycalculix.selector + :members: + :undoc-members: + :show-inheritance: + +pycalculix.version module +------------------------- + +.. automodule:: pycalculix.version + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: pycalculix + :members: + :undoc-members: + :show-inheritance: diff --git a/dist/documentation/_static/alabaster.css b/dist/documentation/_static/alabaster.css new file mode 100644 index 0000000..0eddaeb --- /dev/null +++ b/dist/documentation/_static/alabaster.css @@ -0,0 +1,701 @@ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Georgia, serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Make nested-list/multi-paragraph items look better in Releases changelog + * pages. Without this, docutils' magical list fuckery causes inconsistent + * formatting between different release sub-lists. + */ +div#changelog > div.section > ul > li > p:only-child { + margin-bottom: 0; +} + +/* Hide fugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/dist/documentation/_static/basic.css b/dist/documentation/_static/basic.css new file mode 100644 index 0000000..ea6972d --- /dev/null +++ b/dist/documentation/_static/basic.css @@ -0,0 +1,764 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > p:first-child, +td > p:first-child { + margin-top: 0px; +} + +th > p:last-child, +td > p:last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist td { + vertical-align: top; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +li > p:first-child { + margin-top: 0px; +} + +li > p:last-child { + margin-bottom: 0px; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > p:first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0.5em; + content: ":"; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +div.code-block-caption { + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +div.code-block-caption + div > div.highlight > pre { + margin-top: 0; +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + padding: 1em 1em 0; +} + +div.literal-block-wrapper div.highlight { + margin: 0; +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +code.descclassname { + background-color: transparent; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: relative; + left: 0px; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/dist/documentation/_static/custom.css b/dist/documentation/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/dist/documentation/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/dist/documentation/_static/doctools.js b/dist/documentation/_static/doctools.js new file mode 100644 index 0000000..b33f87f --- /dev/null +++ b/dist/documentation/_static/doctools.js @@ -0,0 +1,314 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { + this.initOnKeyListeners(); + } + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated === 'undefined') + return string; + return (typeof translated === 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated === 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keyup(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box or textarea + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/dist/documentation/_static/documentation_options.js b/dist/documentation/_static/documentation_options.js new file mode 100644 index 0000000..d2dc79f --- /dev/null +++ b/dist/documentation/_static/documentation_options.js @@ -0,0 +1,10 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '1.1.4', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + FILE_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false +}; \ No newline at end of file diff --git a/dist/documentation/_static/file.png b/dist/documentation/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/dist/documentation/_static/file.png differ diff --git a/dist/documentation/_static/jquery-3.4.1.js b/dist/documentation/_static/jquery-3.4.1.js new file mode 100644 index 0000000..773ad95 --- /dev/null +++ b/dist/documentation/_static/jquery-3.4.1.js @@ -0,0 +1,10598 @@ +/*! + * jQuery JavaScript Library v3.4.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2019-05-01T21:04Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.4.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a global context + globalEval: function( code, options ) { + DOMEval( code, { nonce: options && options.nonce } ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.4 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2019-04-08 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) && + + // Support: IE 8 only + // Exclude object elements + (nodeType !== 1 || context.nodeName.toLowerCase() !== "object") ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && rdescend.test( selector ) ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = (elem.ownerDocument || elem).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID filter and find + if ( support.getById ) { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( el ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( el ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( el ) { + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( el ) { + return el.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( typeof elem.contentDocument !== "undefined" ) { + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + +var swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE <=9 only + option: [ 1, "" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + } ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + // Support: IE 9-11 only + // Also use offsetWidth/offsetHeight for when box sizing is unreliable + // We use getClientRects() to check for hidden/disconnected. + // In those cases, the computed value can be trusted to be border-box + if ( ( !support.boxSizingReliable() && isBorderBox || + val === "auto" || + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = Date.now(); + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + + +jQuery._evalUrl = function( url, options ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Index

+ +
+ _ + | A + | B + | C + | D + | E + | F + | G + | H + | I + | L + | M + | N + | O + | P + | R + | S + | T + | U + | V + | W + | X + | Y + | Z + +
+

_

+ + + +
+ +

A

+ + + +
+ +

B

+ + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + +
+ +

W

+ + + +
+ +

X

+ + +
+ +

Y

+ + +
+ +

Z

+ + + +
+ + + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/index.html b/dist/documentation/index.html new file mode 100644 index 0000000..dda62f9 --- /dev/null +++ b/dist/documentation/index.html @@ -0,0 +1,140 @@ + + + + + + + Welcome to pycalculix’s documentation! — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/documentation/objects.inv b/dist/documentation/objects.inv new file mode 100644 index 0000000..7654344 Binary files /dev/null and b/dist/documentation/objects.inv differ diff --git a/dist/documentation/py-modindex.html b/dist/documentation/py-modindex.html new file mode 100644 index 0000000..0f17fad --- /dev/null +++ b/dist/documentation/py-modindex.html @@ -0,0 +1,199 @@ + + + + + + + Python Module Index — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Python Module Index

+ +
+ p +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ p
+ pycalculix +
    + pycalculix.base_classes +
    + pycalculix.cadimporter +
    + pycalculix.components +
    + pycalculix.connectors +
    + pycalculix.environment +
    + pycalculix.feamodel +
    + pycalculix.geometry +
    + pycalculix.installer +
    + pycalculix.loads +
    + pycalculix.material +
    + pycalculix.mesh +
    + pycalculix.partmodule +
    + pycalculix.problem +
    + pycalculix.results_file +
    + pycalculix.selector +
    + pycalculix.version +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/pycalculix.html b/dist/documentation/pycalculix.html new file mode 100644 index 0000000..0863720 --- /dev/null +++ b/dist/documentation/pycalculix.html @@ -0,0 +1,6282 @@ + + + + + + + pycalculix package — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

pycalculix package

+
+

Submodules

+
+
+

pycalculix.base_classes module

+

This module stores base classes and functions.

+
+
+pycalculix.base_classes.RESFILEDS
+

stores results fields under results type

+
+
key (str):
    +
  • ‘displ’: displacement

  • +
  • ‘stress’: stress

  • +
  • ‘strain’: strain

  • +
  • ‘force’: force

  • +
+
+
value (list):
    +
  • ‘displ’ = ‘ux’,’uy’,’uz’,’utot’

  • +
  • ‘stress’ = ‘Sx’,’Sy’,’Sz’,’Sxy’,’Syz’,’Szx’,’Seqv’,’S1’,’S2’,’S3’

  • +
  • ‘strain’ = ‘ex’,’ey’,’ez’,’exy’,’eyz’,’ezx’,’eeqv’,’e1’,’e2’,’e3’

  • +
  • ‘force’ = ‘fx’,’fy’,’fz’

  • +
+
+
+
+
Type
+

dict

+
+
+
+ +
+
+pycalculix.base_classes.FIELDTYPE
+

the inverse dict of RESFIELDS

+

For example: key ‘ux’ –> value: ‘displ’

+
+
Type
+

dict

+
+
+
+ +
+
+class pycalculix.base_classes.Idobj[source]
+

Bases: object

+

Makes an object that stores an id number.

+

This is a base class for nodes, lines, areas etc.

+
+
+id
+

the unique id number for the item

+
+
Type
+

int

+
+
+
+ +
+
+set_id(id)[source]
+

Sets the id number

+
+ +
+ +
+
+class pycalculix.base_classes.Itemlist[source]
+

Bases: list

+

Makes a custom list used to store lists of non-mesh items.

+

non-mesh items = Point, Line, Arc, Area, Part, Components, Loads +This class allows us to automatically assign id numbers to items in the +list. Thes id numbers are needed when we send geometry out for meshing. +All instances of this class start as empty lists.

+
+
+append(item)[source]
+

Adds an item to the list and sets the item’s id.

+
+
Parameters
+

item (Point or Line or Arc or Area or Part) – item to add to the list

+
+
+
+ +
+
+get_ids()[source]
+

Returns a list of ids. Loops through all items in self.

+
+ +
+
+get_next_id()[source]
+

Returns the next unused id.

+
+ +
+ +
+
+class pycalculix.base_classes.Meshlist[source]
+

Bases: list

+

Makes a custom list used to store lists of nodes and elements.

+

All instances of this class start as empty lists.

+
+
+get_maxid()[source]
+

Returns the max id number in the list.

+
+ +
+
+get_minid()[source]
+

Returns the min id number in the list.

+
+ +
+
+idget(idnum)[source]
+

Returns an item with the passed id number.

+
+
Parameters
+

idnum (int) – passed id number, we want the item that has this

+
+
Returns
+

returns item if found, None if not found

+
+
Return type
+

item or None

+
+
+
+ +
+
+set_minid(val)[source]
+

Sets the min id to the passed val.

+
+
Parameters
+

val (int) – New min id number in the list

+
+
+
+ +
+ +
+
+pycalculix.base_classes.chunk_list(inlist, size)[source]
+

Returns a list of lists where each list <= size length.

+

Splits inlist into list of lists where each child list len <= size.

+
+
Parameters
+
    +
  • inlist (list) – list that we want cut into smaller lists

  • +
  • size (int) – max length of small lists returned

  • +
+
+
Returns
+

list of list where each child list <= size length

+
+
Return type
+

res (list of lists)

+
+
+
+ +
+
+pycalculix.base_classes.listify(items)[source]
+

Returns a list of items. If items is an object it puts it in a list.

+

If a list is passed, it returns the list without changing it. +If an object is passed, it returns a list with the object in it.

+
+
Parameters
+

items (object or list) – item or list to convert to a list

+
+
+
+ +
+
+pycalculix.base_classes.plot_finish(plt, fname, display)[source]
+

Display and or save plot.

+
+ +
+
+pycalculix.base_classes.plot_set_bounds(plt, axials, radials)[source]
+

Sets the axial and radial bounds of the shown plot.

+
+ +
+
+

pycalculix.cadimporter module

+

This module stores the CadImporter class, which is used to load CAD parts.

+
+
+class pycalculix.cadimporter.CadImporter(feamodel, fname='', layer=-1, swapxy=False, scale='')[source]
+

Bases: object

+

Makes an object which can import cad parts.

+
+
Parameters
+
    +
  • feamodel (FeaModel) – model that we want to import parts into

  • +
  • layer (int) – layer to import, all entities will be flattened +to one plane. -1 mean all layers, any other number is a specific layer

  • +
  • swapxy (bool) – True rotates the part from x axial to y axial

  • +
  • scale (str) –

    string telling the unit conversion scalar

    +
      +
    • Any option of ‘fromunit-tounit’ using the below units

    • +
    • mm, m, in, ft

    • +
    • Examples ‘mm-m’ ‘m-in’ ‘ft-mm’ ‘in-ft’

    • +
    • Default value is ‘’ and does not apply a scale factor

    • +
    +

  • +
+
+
+
+
+__fea
+

model

+
+
Type
+

FeaModel

+
+
+
+ +
+
+__fname
+

file we want to import

+
+
Type
+

str

+
+
+
+ +
+
+__layer
+

layer to import

+
+
Type
+

int

+
+
+
+ +
+
+__swapxy
+

If true, swap part from xy to yx orientation

+
+
Type
+

bool

+
+
+
+ +
+
+load()[source]
+

Loads the self.__fname cad file

+
+
Returns
+

list of Part

+
+
Return type
+

list

+
+
+
+ +
+ +
+
+

pycalculix.components module

+

This module defines the Component class, which is used to make components.

+

Components are collections that loads are applied to.

+
+
+class pycalculix.components.Component(item_list, ctype, cname='')[source]
+

Bases: pycalculix.base_classes.Idobj

+

Makes a component to store loads, boundary conditions, matl, thickness.

+

A component must store a list of items where the type is identical for +all items.

+
+
Parameters
+
    +
  • item_list (list) – list of items to store

  • +
  • ctype (str) –

    type of component. Options:

    +
      +
    • ’nodes’: all element nodes (corner and mid-side)

    • +
    • ’n1’: element corner nodes

    • +
    • ’faces’: faces

    • +
    • ’elements’: elements

    • +
    +

  • +
  • cname (str) – component name

  • +
+
+
+
+
+ccx()[source]
+

Writes a component for Calculix ccx solver.

+

Returns a list strings, where each item is a line to write +to a Calculix .inp text file.

+
+

Note

+

Only node and element components should use this.

+
+
+ +
+
+get_children()[source]
+

Returns a list the child ctype items of the current component.

+
+ +
+
+get_name()[source]
+

Returns the component name created with its id number.

+
+ +
+
+set_name(name)[source]
+

Set the component name to the passed name.

+
+
Parameters
+

name (str) – passed name, component name is changed to this

+
+
+
+ +
+
+write_cgx()[source]
+

Writes a component for Calculix cgx prerocessor.

+

Returns a list strings, where each item is a line to write +to a Calculix .fbd text file.

+
+

Note

+

Only line and point node components should use this.

+
+
+ +
+
+write_gmsh()[source]
+

Writes a component for gmsh mesher.

+

Returns a list strings, where each item is a line to write +to a Calculix .geo text file.

+
+

Note

+

Only line and point node components should use this.

+
+
+ +
+ +
+
+

pycalculix.connectors module

+

This module stores connector classes, like Contact.

+
+
+class pycalculix.connectors.Contact(master_comp, slave_comp, surf_int, surf_to_surf=True)[source]
+

Bases: pycalculix.base_classes.Idobj

+

Makes a contact which will be between lines which have faces on them.

+
+
Parameters
+
    +
  • master_comp (Component) – component of master element faces

  • +
  • slave_comp (Component) – component of slave element faces

  • +
  • surf_int (SurfaceInteraction) – object which stores closure behavior

  • +
  • surf_to_surf (bool) – if True surface to surface is used, if False node +to surface is used

  • +
+
+
+
+
+ccx()[source]
+

Writes the contact pair for the ccx solver.

+
+ +
+
+property name
+

Contact name.

+
+ +
+ +
+
+class pycalculix.connectors.SurfaceInteraction(int_type, *args)[source]
+

Bases: pycalculix.base_classes.Idobj

+

Makes a surface interaction object.

+
+
Parameters
+
    +
  • int_type (str) –

    interaction type

    +
      +
    • ’EXPONENTIAL’

    • +
    • ’LINEAR’

    • +
    +

  • +
  • *args

    following arguments

    +
      +
    • +
      int_type = ‘EXPONENTIAL’
        +
      • c0, then p0 must be passed

      • +
      +
      +
      +
    • +
    • +
      int_type = ‘LINEAR’
        +
      • k must be passed

      • +
      +
      +
      +
    • +
    +

  • +
+
+
+
+
+ccx()[source]
+

Writes the surface interaction for the ccx solver.

+
+ +
+
+property name
+

SurfaceInteraction name.

+
+ +
+ +
+
+

pycalculix.environment module

+

This module sets the dpi and the paths to gmsh, ccx, and cgx.

+
+
+pycalculix.environment.DPI
+

if high dpi windows 8 monitor value is set, otherwise +value is None.

+
+
Type
+

None or float

+
+
+
+ +
+
+pycalculix.environment.CCX
+

path to Calculix ccx, the sovler.

+
+
Type
+

str

+
+
+
+ +
+
+pycalculix.environment.CGX
+

path to Calculix cgx, the preprocessor/postprocessor/mesher.

+
+
Type
+

str

+
+
+
+ +
+
+pycalculix.environment.GMSH
+

path to the Gmsh mesher.

+
+
Type
+

str

+
+
+
+ +
+
+class pycalculix.environment.OSVERSIONINFOEXW[source]
+

Bases: _ctypes.Structure

+

Returns object w/ attributes that will identify window os version

+
+
+dwBuildNumber
+

Structure/Union member

+
+ +
+
+dwMajorVersion
+

Structure/Union member

+
+ +
+
+dwMinorVersion
+

Structure/Union member

+
+ +
+
+dwOSVersionInfoSize
+

Structure/Union member

+
+ +
+
+dwPlatformId
+

Structure/Union member

+
+ +
+
+szCSDVersion
+

Structure/Union member

+
+ +
+
+wProductType
+

Structure/Union member

+
+ +
+
+wReserved
+

Structure/Union member

+
+ +
+
+wServicePackMajor
+

Structure/Union member

+
+ +
+
+wServicePackMinor
+

Structure/Union member

+
+ +
+
+wSuiteMask
+

Structure/Union member

+
+ +
+ +
+
+pycalculix.environment.get_dpi()[source]
+

Returns an int of current DPI for windows or None.

+
+ +
+
+pycalculix.environment.get_paths()[source]
+

Returns a list of paths to: [ccx, cgx, gmsh].

+
+ +
+
+pycalculix.environment.get_version()[source]
+

Get’s the OS version. Returns a float of OS_MAJOR.OS_MINOR

+
+ +
+
+

pycalculix.feamodel module

+

This module stores the FeaModel class.

+

This class is the highest level object in a pycalculix program. +It stores all parts, loads, constraints, mesh, problem, and results_file +objects.

+
+
+class pycalculix.feamodel.FeaModel(model_name, ccx=None, cgx=None, gmsh=None)[source]
+

Bases: object

+

Makes a FeaModel instance.

+

Parts, area, lines, arcs, points, nodes, elements, faces, models, +components, and loads are stored in this object.

+
+
Parameters
+
    +
  • model_name (str) – project name for this FeaModel, this is a file prefix

  • +
  • ccx (None or str) – path to Calculix ccx solver, pass this when you want +to overwite the default program location. +None means the default envionment.CCX is used.

  • +
  • cgx (None or str) – path to Calculix cgx mesher, pass this when you want +to overwite the default program location. +None means the default envionment.CGX is used.

  • +
  • gmsh (None or str) – path to gmsh mesher, pass this when you want +to overwite the default program location. +None means the default envionment.GMSH is used.

  • +
+
+
+
+
+fname
+

FeaModel project file name prefix

+
+
Type
+

str

+
+
+
+ +
+
+points
+

list of all Point

+
+
Type
+

Itemlist

+
+
+
+ +
+
+lines
+

list of all Line and Arc

+
+
Type
+

Itemlist

+
+
+
+ +
+
+signlines
+

list of all SignLine and SignArc

+
+
Type
+

Itemlist

+
+
+
+ +
+
+lineloops
+

list of all LineLoop, each contains SignLine SignArc

+
+
Type
+

Itemlist

+
+
+
+ +
+
+areas
+

list of all Area, each contains LineLoop(s)

+
+
Type
+

Itemlist

+
+
+
+ +
+
+parts
+

list of all Part

+
+
Type
+

Itemlist

+
+
+
+ +
+
+matls
+

list of all materials

+
+
Type
+

Itemlist

+
+
+
+ +
+
+components
+

list of all components

+
+
Type
+

Itemlist

+
+
+
+ +
+
+loads
+

a dictionary of loads

+
+
Key (float): the load time point
+
loads[time] = list of loads for that time step
+
See method set_time to change the current time.
+
Time = 0.0 stores constant loads, such as:
+
+
material, thickness
+
+
+
+
Type
+

dict

+
+
+
+ +
+
+contacts
+

list of contacts

+
+
Type
+

Itemlist

+
+
+
+ +
+
+surfints
+

list of surface interactions

+
+
Type
+

Itemlist

+
+
+
+ +
+
+problems
+

list of problems

+
+
Type
+

Itemlist

+
+
+
+ +
+
+nodes
+

list of all mesh nodes

+
+
Type
+

Meshlist

+
+
+
+ +
+
+eshape
+

element shape

+
    +
  • ‘quad’: quadrilateral elements (Default)

  • +
  • ‘tri’: triangle elements

  • +
+
+
Type
+

str

+
+
+
+ +
+
+eorder
+

element order, 1 or 2

+
    +
  • 1: elements meshed with corner nodes only

  • +
  • 2: (Default) elements meshed with corner and midside nodes

  • +
+
+
Type
+

int

+
+
+
+ +
+
+elements
+

list of all mesh elements

+
+
Type
+

Meshlist

+
+
+
+ +
+
+faces
+

list of all element faces, includes non-exterior ones

+
+
Type
+

list

+
+
+
+ +
+
+view
+

currently selected items

+
+
+parts
+

list of selected parts

+
+ +
+
+areas
+

list of selected areas

+
+ +
+
+lines
+

list of selected signed lines or arcs

+
+ +
+
+points
+

list of selected points

+
+ +
+
+elements
+

list of selected elements

+
+ +
+
+faces
+

list of selected faces

+
+ +
+
+nodes
+

list of selected nodes

+
+ +
+
Type
+

Selector

+
+
+
+ +
+
+time
+

current model time value, defaults to 1.0

+
+
Type
+

float

+
+
+
+ +
+
+units
+

a dict to store units for different fields

+
+
Keys:
    +
  • ‘displ’: displacement or location

  • +
  • ‘force’: force

  • +
  • ‘stress’: stress

  • +
  • ‘temp’: temperature

  • +
  • ‘density’: density (mass/volume)

  • +
  • ‘time’: time

  • +
+
+
+
+
Returns
+

Text string describing the units for the given key field.

+

See the set_units method.

+

For example when units have been set to metric, the below +values are returned.

+
    +
  • ’dist’ –> ‘m’

  • +
  • ’density’ –> ‘kg/(m^3)’

  • +
  • ’time’ –> ‘s’

  • +
+

+
+
Type
+

dict

+
+
+
+ +
+
+get_item(item)[source]
+

Returns an item given a string identifying the item.

+
+
Parameters
+

item (str) – ‘A0’ or ‘P0’ or ‘L0’ etc.

+
+
+
+ +
+
+get_items(items)[source]
+

Returns a list of correctly typed items.

+

Input can be a single string or item, or a list of strings identifying +items.

+
+ +
+
+get_units(*args)[source]
+

Returns units for the passed arguments. Accepts and returns a list +of strings.

+
+
Options for inputs:
    +
  • ‘dist’

  • +
  • ‘displ’ (same units as ‘dist’)

  • +
  • ‘force’

  • +
  • ‘stress’

  • +
  • ‘temp’

  • +
  • ‘density’

  • +
  • ‘time’

  • +
+
+
+
+ +
+
+make_matl(name)[source]
+

Makes and returns a new material.

+
+
Parameters
+

name (str) – the material’s name

+
+
+
+ +
+
+make_part()[source]
+

Makes and returns a new part.

+
+ +
+
+make_problem(problem_type='struct', parts='all')[source]
+

Makes and returns a new problem, which can be solved.

+
+
Parameters
+
    +
  • problem_type (str) – problem type, options: +‘struct’: structural

  • +
  • parts (str Part or list of Part) –

    Parts the model will analyze.

    +
    +
    Options:
      +
    • ’all’: add all parts. This is the default.

    • +
    • Part: add the single part

    • +
    • list of Part: add these parts

    • +
    +
    +
    +

  • +
+
+
+
+ +
+
+mesh(size=1.0, meshmode='fineness', mesher='gmsh')[source]
+

Meshes all parts.

+
+
Parameters
+
    +
  • size (float) –

      +
    • +
      if meshmode == ‘fineness’ (default):
        +
      • mesh size is adapted to geometry size

      • +
      • set size = 0.0001 - 1.0, to define how fine the mesh is.

      • +
      • Low numbers are very fine, higher numbers are coarser.

      • +
      +
      +
      +
    • +
    • +
      if meshmode == ‘esize’:
        +
      • element size is kept constant

      • +
      • choose it depending on geometry size

      • +
      • it should be reduced e.g. at arcs with small radius, by calling line.esize function

      • +
      +
      +
      +
    • +
    +

  • +
  • meshmode (str) –

      +
    • ‘fineness’: adapt mesh size to geometry

    • +
    • ’esize’: keep explicitly defined element size

    • +
    +

    meshmode is changed to ‘esize’ is used if esize property is set to points or lines

    +

  • +
  • mesher (str) –

    the mesher to use

    +
      +
    • ’gmsh’: mesh with Gmsh, this is reccomended, it allows holes

    • +
    • ’cgx’: mesh with Calculix cgx, it doesn’t allow holes

    • +
    +

  • +
+
+
+
+ +
+
+plot_areas(fname='', display=True, title='Areas', label=True)[source]
+

Plots selected areas

+

Defaults to displaying labels on: areas and filling all part areas.

+
+
Parameters
+
    +
  • fname (str) – png image file prefix, if given, image will be saved

  • +
  • display (bool) – if True, interactive plot will be shown, if False +plot will not be shown. Default = True

  • +
  • title (str) – the plot’s title

  • +
  • label (bool) – if True all areas will be labeled

  • +
+
+
+
+ +
+
+plot_constraints(fname='', display=True)[source]
+

Plots the constraints on the passed part(s) or selected items.

+

This is an element and node plot, with arrows showing displacement +constraints.

+
+
Parameters
+
    +
  • fname (str) – png image file prefix, if given, image will be saved

  • +
  • display (bool) – if True, interactive plot will be shown, if False +plot will not be shown. Default = True

  • +
+
+
+
+ +
+
+plot_elements(fname='', display=True, title='Elements', enum=False, nshow=False, nnum=False)[source]
+

Plots the selected elements.

+
+
Parameters
+
    +
  • fname (str) – png image file prefix, if given, image will be saved

  • +
  • display (bool) – if True, interactive plot will be shown, if False +plot will not be shown

  • +
  • title (str) – the plot’s title

  • +
  • enum (bool) – if True element numbers are plotted

  • +
  • nshow (bool) – True=plot nodes, False=don’t plot them

  • +
  • nnum (bool) – if True node numbers are plotted

  • +
+
+
+
+ +
+
+plot_geometry(fname='', display=True, title='Geometry', pnum=True, lnum=True, anum=True, afill=True)[source]
+

Plots selected geometry items: areas, lines, points.

+

Defaults to displaying labels on: areas, lines, and points, and filling +in the areas.

+
+
Parameters
+
    +
  • fname (str) – png image file prefix, if given, image will be saved

  • +
  • display (bool) – if True, interactive plot will be shown, if False +plot will not be shown. Default = True

  • +
  • title (str) – the plot’s title

  • +
  • pnum (bool) – if True all Point objects will be labeled

  • +
  • lnum (bool) – if True all Line and Arc objects will be labeled

  • +
  • anum (bool) – if True all Area objects will be labeled

  • +
  • afill (bool) – if True all Area objects will be filled

  • +
+
+
+
+ +
+
+plot_lines(fname='', display=True, title='Lines', label=True)[source]
+

Plots selected lines and arcs

+

Defaults to displaying labels on: lines and arcs

+
+
Parameters
+
    +
  • fname (str) – png image file prefix, if given, image will be saved

  • +
  • display (bool) – if True, interactive plot will be shown, if False +plot will not be shown. Default = True

  • +
  • title (str) – the plot’s title

  • +
  • label (bool) – if True all lines and arcs will be labeled

  • +
+
+
+
+ +
+
+plot_multiple(fname='', display=True, title='', styledict={'items': ['points, lines, areas'], 'labels': ['points', 'lines', 'areas', 'parts']})[source]
+

Plots items of type styledict[‘items’], labels styledict[‘labels’]

+

Only the items currently selected will be plotted.

+
+ +
+
+plot_nodes(fname='', display=True, title='Nodes', nnum=False)[source]
+

Plots the selected nodes.

+
+
Parameters
+
    +
  • fname (str) – png image file prefix, if given, image will be saved

  • +
  • display (bool) – if True, interactive plot will be shown, if False +plot will not be shown

  • +
  • title (str) – the plot’s title

  • +
  • nnum (bool) – if True node numbers are plotted

  • +
+
+
+
+ +
+
+plot_parts(fname='', display=True, title='Parts', label=True)[source]
+

Plots selected parts

+

Defaults to displaying labels on: parts and filling all part areas.

+
+
Parameters
+
    +
  • fname (str) – png image file prefix, if given, image will be saved

  • +
  • display (bool) – if True, interactive plot will be shown, if False +plot will not be shown. Default = True

  • +
  • title (str) – the plot’s title

  • +
  • label (bool) – if True all Part objects will be labeled

  • +
+
+
+
+ +
+
+plot_points(fname='', display=True, title='Points', label=True)[source]
+

Plots selected points

+

Defaults to displaying labels on: points

+
+
Parameters
+
    +
  • fname (str) – png image file prefix, if given, image will be saved

  • +
  • display (bool) – if True, interactive plot will be shown, if False +plot will not be shown. Default = True

  • +
  • title (str) – the plot’s title

  • +
  • label (bool) – if True all points will be labeled

  • +
+
+
+
+ +
+
+plot_pressures(fname='', display=True)[source]
+

Plots the load step pressures on the passed part(s) or selected +elements.

+

This is an element plot, with arrows showing pressure magnitude and +directions.

+
+
Parameters
+
    +
  • fname (str) – png image file prefix, if given, image will be saved

  • +
  • display (bool) – if True, interactive plot will be shown, if False +plot will not be shown. Default = True

  • +
+
+
+
+ +
+
+print_summary()[source]
+

Prints a summary of the items in the database.

+
+ +
+
+register(item)[source]
+

Adds an item to the feamodel.

+

Item is added to the correct list and its id is updated. +Allowed Items:

+
    +
  • Point

  • +
  • Line

  • +
  • Arc

  • +
  • SignLine

  • +
  • SignArc

  • +
  • LineLoop

  • +
  • Area

  • +
  • Part

  • +
+
+ +
+
+scale(unitstr, point_first=None, point_last=None)[source]
+

Scales the points from [point_first, …, point_last] using unitstr

+

If point_first and point_last are both None, all points are scaled.

+
+
Parameters
+
    +
  • unitstr (str) –

    string scalar ‘fromunit-tounit’ using the below units:

    +
      +
    • mm, m, in, ft

    • +
    • Examples ‘mm-m’ ‘m-in’ ‘ft-mm’ ‘in-ft’

    • +
    • Default value is ‘’ and does not apply a scale factor

    • +
    +

  • +
  • point_first (Point or None or str) – the first point to scale, +string point names may be passed

  • +
  • point_last (Point or None or str) – the last point to scale, +string point names may be passed

  • +
+
+
+
+ +
+
+set_constr(ltype, items, axis, val=0.0)[source]
+

Sets a displacement constraint on the passed item(s).

+
+
Parameters
+
    +
  • ltype (str) –

    ‘fix’ or ‘displ’

    +
      +
    • ’fix’: val arg should not be passed

    • +
    • ’displ’: val arg must be passed

    • +
    +

  • +
  • items (str, SignLine, SignArc, Point or list) –

    item(s) to apply the +constraint on

    +
      +
    • str: this string must be a line or point name

    • +
    • +
      SignLine or SignArc: the constraint will be applied

      to this item

      +
      +
      +
    • +
    • Point: the constraint will be applied on this item

    • +
    • +
      list of SignLine or SignArc: the constraint will be appplied

      on these

      +
      +
      +
    • +
    • list of Point: the constraint will be appplied on these

    • +
    • +
      list of str names: pass line or point names, constr

      applied on them

      +
      +
      +
    • +
    +

  • +
  • axis (str) – load axis, ‘x’ or ‘y’

  • +
  • val (float) – displacement value, defaults to 0.0, only needs to be +set when ltype = ‘displ’

  • +
+
+
+
+ +
+
+set_contact_linear(master_lines, slave_lines, kval, many_si=False)[source]
+

Sets contact between master and slave lines.

+

Slave lines are on the more compliant or more curved object.

+
+
Parameters
+
    +
  • master_lines (list) – list of SignLine or SignArc

  • +
  • slave_lines (list) – list of SignLine or SignArc

  • +
  • kval (float) – stiffness, 5 to 50 times the youngs modulus +of the touching matl

  • +
  • many_si (bool) – True, make unique surface interaction for every contact +False, use existing surface interaction if we can

  • +
+
+
+
+ +
+
+set_ediv(items, ediv)[source]
+

Sets the number of elements on the passed line.

+
+
Parameters
+
    +
  • items (str or SignLine or SignArc or list) –

    lines to set ediv on

    +
      +
    • str: ‘L0’

    • +
    • list of str [‘L0’, ‘L1’]

    • +
    • list of SignLine or SignArc part.bottom or part.hole[-1]

    • +
    +

  • +
  • ediv (int) – number of elements to mesh on the line

  • +
+
+
+
+ +
+
+set_eshape(eshape='quad', eorder=2)[source]
+

Sets the element shape and order to use when meshing the model.

+
+
Parameters
+
    +
  • eshape (str) –

    element shape

    +
      +
    • ’quad’: quadrilaterials

    • +
    • ’tri’: triangles

    • +
    +

  • +
  • eorder (int) –

    element order, default=2

    +
      +
    • 1: corner nodes only (3 and 4 noded elements)

    • +
    • 2: corder nodes and midside nodes (6 and 8 noded elements)

    • +
    +

  • +
+
+
+
+ +
+
+set_esize(items, esize)[source]
+

Sets the element size on the passed line.

+
+
Parameters
+
    +
  • items (str or SignLine or SignArc or list) –

    lines or points to set esize on

    +
      +
    • str: ‘L0’

    • +
    • list of str [‘L0’, ‘L1’, ‘P3’]

    • +
    • list of SignLine or SignArc part.bottom or part.hole[-1]

    • +
    +

  • +
  • esize (float) – size of the mesh elements on the line

  • +
+
+
+
+ +
+
+set_etype(etype, items, thick=None)[source]
+

Sets the element type, and thickness on areas or parts.

+
+
Parameters
+
    +
  • etype (str) –

    element type

    +
      +
    • ’plstress’: plane stress

    • +
    • ’plstrain’: plane strain

    • +
    • ’axisym’: axisymmetric

    • +
    +

  • +
  • items (str or Area or Part or list) –

    set element type on these

    +
      +
    • str: string name of Area or Part item. Example: ‘A0’, ‘PART0’

    • +
    • Area: set element type on the elements in this area

    • +
    • Part: set element type on the elements in this part

    • +
    • list of Area or Part: set element type on their elements

    • +
    +

  • +
  • thick (float or None) –

    element thickness

    +
      +
    • None: default, used for axisymmetric element type

    • +
    • +
      float: thickness value to use for plane stress or plane

      strain elements

      +
      +
      +
    • +
    +

  • +
+
+
+
+ +
+
+set_fluid_press(items, rho, g, xo, po)[source]
+

This sets a fluid presure load on items.

+

This fluid pressure is dependednt on the x axis. +g must be positive.

+
+
    +
  • P = f(x)

  • +
  • P(xo) = po

  • +
  • P(x) = po + rho*g*(xo - x)

  • +
+
+
+
Parameters
+
    +
  • items (str or Line or Arc or list) –

    items to set pressure on

    +
      +
    • str: string name of Line or Arc item, for example ‘L0’

    • +
    • Line or Arc: set presssure on this

    • +
    • list or Line or Arc: set pressure on these

    • +
    +

  • +
  • rho (float) – fluid density in mass/volume

  • +
  • g (+float) – gravity in dist/(t^2), MUST BE POSITIVE

  • +
  • xo (float) – sea level height, MUST BE POSITIVE

  • +
  • po (float) – sea level pressure, MUST BE POSITIVE

  • +
+
+
+
+ +
+
+set_gravity(grav, items)[source]
+

Sets gravity on the elements in items.

+

Assumes gravity acts in the -x direction with magnitude grav.

+
+
Parameters
+
    +
  • grav (float) – gravity acceleration, MUST BE POSTIVE

  • +
  • items (Area or Part or list) –

    items gravity acts on

    +
      +
    • Area: gravity acts on elements in this area

    • +
    • Part: gravity acts on elements in this part

    • +
    • list of Part or Area: gravity acts on their elements

    • +
    +

  • +
+
+
+
+ +
+
+set_load(ltype, items, lval, ldir=None)[source]
+

Sets a pressure or force load on line(s).

+

The force load is divided by the number of nodes and applied on +each node.

+

The pressure load is applied to the child faces under the line. +Positive is compression, negative is tension.

+
+
Parameters
+
    +
  • ltype (str) – ‘press’ or ‘force’

  • +
  • items (str or Line or Arc or list) –

    items to set load on

    +
      +
    • str: string name of Line or Arc item, for example ‘L0’

    • +
    • Line or Arc: set load on this

    • +
    • list or Line or Arc: set load on these

    • +
    +

  • +
  • lval (float) –

    load value.

    +
      +
    • For ltype = ‘press’ this is in force/area units

    • +
    • For ltype = ‘force’ this is in force units

    • +
    +

  • +
  • ldir (None or str) –

    load direction. Defaults to None

    +
      +
    • str: when ltype=’load’, we need to set ldir to ‘x’ or ‘y’

    • +
    • None: when ltype=’press’

    • +
    +

  • +
+
+
+
+ +
+
+set_matl(matl, items)[source]
+

Sets the material on Part or Area items.

+
+
Parameters
+
    +
  • matl (Material) – material to assign to items

  • +
  • items (Part or Area or list) –

    items that will have matl assigned

    +
      +
    • Part: assign matl to this

    • +
    • Area: assign matl to this

    • +
    • list of Area or Part: assign matl to these

    • +
    +

  • +
+
+
+
+ +
+
+set_radps(radps, items)[source]
+

Sets radians per second rotation load on the items.

+
+
Parameters
+
    +
  • radps (float) – radians per second

  • +
  • items (Area or Part or list) –

    items rotation acts on

    +
      +
    • Area: rotation acts on elements in this area

    • +
    • Part: rotation acts on elements in this part

    • +
    • list of Part or Area: rotation acts on their elements

    • +
    +

  • +
+
+
+
+ +
+
+set_rpm(rpm, items)[source]
+

Sets rpm rotation load on the items.

+
+
Parameters
+
    +
  • rpm (float) – rotation per minute

  • +
  • items (Area or Part or list) –

    items rotation acts on

    +
      +
    • Area: rotation acts on elements in this area

    • +
    • Part: rotation acts on elements in this part

    • +
    • list of Part or Area: rotation acts on their elements

    • +
    +

  • +
+
+
+
+ +
+
+set_time(time)[source]
+

Sets the time in the FeaModel (preprocessor).

+

This time is used when setting loads.

+
+
Parameters
+

time (float) – the time to set

+
+
+
+ +
+
+set_units(dist_unit='m', cfswitch=False)[source]
+

Sets the units that will be displayed when plotting.

+

Picks a unit set based on the passed distance unit. +That unit set is printed to the console when set. +Defaults to MKS units (meter-kilogram-second)

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Distance

Force

Stress

Temperature

Density

Time

‘m’

‘N’

‘Pa’

‘K’

‘kg/(m^3)’

‘s’

‘mm’

‘N’

‘MPa’

‘K’

‘tonne/(mm^3)’

‘s’

‘in’

‘lbf’

‘psi’

‘R’

‘slinch/(in^3)’

‘s’

‘ft’

‘lbf’

‘psf’

‘R’

‘slug/(ft^3)’

‘s’

+

See get_units method or returning text strings based on unit types.

+
+
Parameters
+
    +
  • dist_unit (str) –

    string of distance unit. Options:

    +
      +
    • ’m’: meter

    • +
    • ’mm’: milimeter

    • +
    • ’in’: inch

    • +
    • ’ft’: foot

    • +
    +

  • +
  • cfswitch (bool) –

    Celsius/Fahrenheit temperature switch. +Default is False.

    +

    If True, this switches temperature from K–>C or from R–>F +Default keeps units as shown in above table.

    +

  • +
+
+
+
+ +
+ +
+
+

pycalculix.geometry module

+

This module stores geometry classes, which are used to make parts.

+
+
+class pycalculix.geometry.Arc(start_pt, end_pt, actr)[source]
+

Bases: pycalculix.base_classes.Idobj

+

Makes an arc from points start_pt to end_pt about center actr.

+

Arcs should be 90 degrees maximum.

+
+
Parameters
+
    +
  • start_pt (Point) – first point

  • +
  • end_pt (Point) – second point

  • +
  • actr (Point) – arc center

  • +
+
+
+
+
+points
+

list of the line’s points [start_pt, end_pt]

+
+
Type
+

list of Point

+
+
+
+ +
+
+allpoints
+

[start_pt, end_pt, actr]

+
+
Type
+

list of Point

+
+
+
+ +
+
+actr
+

arc center

+
+
Type
+

Point

+
+
+
+ +
+
+radius
+

radius of the arc

+
+
Type
+

float

+
+
+
+ +
+
+concavity
+
    +
  • ‘concave’: concave arc

  • +
  • ‘convex’: convex arc

  • +
+
+
Type
+

str

+
+
+
+ +
+
+midpt
+

a mid-point between p1 and p2

+
+
Type
+

Point

+
+
+
+ +
+
+ediv
+

number of elements on arc

+
+
Type
+

None or int

+
+
+
+ +
+
+nodes
+

a list of meshed nodes on the line

+
+
Type
+

list of Node

+
+
+
+ +
+
+edge
+

True if arc is a non-shared edge in an area

+
+
Type
+

bool

+
+
+
+ +
+
+signlines
+

list of SignArc that use this Arc

+
+
Type
+

list

+
+
+
+ +
+
+add_signline(signline)[source]
+

Adds a signline to self.signlines.

+
+ +
+
+property allpoints
+

Returns all points including arc center

+
+ +
+
+property areas
+

Returns all areas that signlines use.

+
+ +
+
+coincident(pt)[source]
+

Checks to see if pt is on the arc.

+
+
Parameters
+

pt (Point) – input point to check

+
+
Returns
+

True if pt on arc or False if not

+
+
Return type
+

bool

+
+
+
+ +
+
+get_ang()[source]
+

Returns angle from beginning to end of arc from arc ctr in degrees

+

Notes

+

Answer is between 180 and -180 +Positive is concave +Negative is convex

+
+ +
+
+get_ang_rad()[source]
+

Returns angle from beginning to end of arc from arc ctr in rad

+

Notes

+

Answer is between pi and -pi +Positive is concave +Negative is convex

+
+ +
+
+get_concavity(clockwise=True)[source]
+

Returns concavity of this arc, ‘concave’ or ‘convex’.

+
+ +
+
+get_name()[source]
+

Returns the arc name.

+
+ +
+
+get_perp_vec(pt)[source]
+

Returns vector perpendicular to current arc.

+
+
Right hand rule is used:

X+ is the direction of the arc from start to end. +Z+ is out of the page +Y+ is the direction of the perpendicular vector

+
+
+
+
Parameters
+

pt (Point) – point on arc where we want the perpendicular vector

+
+
Returns
+

perpendicular vector

+
+
Return type
+

resv (Point)

+
+
+
+ +
+
+get_pt_at(nondim)[source]
+

Returns point on arc between start and end point at nondim dist.

+
+
Parameters
+

nondim (float) – distance between start and end point, 0=start, 1=end

+
+
Returns
+

requested point on arc

+
+
Return type
+

res (Point)

+
+
+
+ +
+
+get_tan_vec(point)[source]
+

Returns vector tangent to the current arc.

+

Vector points from the passed point to a unit location further away.

+
+
Parameters
+

point (Point) – start point

+
+
Returns
+

vector that is tangent to the current line

+
+
Return type
+

resv (Point)

+
+
+
+ +
+
+get_verts_codes(plot=True)[source]
+

Returns a list of [verts, codes] vertices for plotting.

+
+ +
+
+intersects(other)[source]
+

Checks if other line intersects this arc.

+
+
Parameters
+

other (Line or Arc) – other line to check

+
+
Returns
+

Point: intersection point +None: None if lines don’t intersect

+
+
Return type
+

Point or None

+
+
+
+ +
+
+length()[source]
+

Returns the length of the arc.

+
+ +
+
+mid()[source]
+

Caclulates and returns the midpoint of the arc.

+
+ +
+
+pt(ind)[source]
+

Returns the start or end point of the arc.

+
+
Parameters
+

ind (int) – index of the point we want, 0=start, 1=end

+
+
Returns
+

requested Point

+
+
Return type
+

Point

+
+
+
+ +
+
+reverse()[source]
+

Reverses self.

+
+ +
+
+save_to_points()[source]
+

This stores this line in the line’s child points.

+
+ +
+
+set_ediv(ediv)[source]
+

Sets the number of element divisions on the arc when meshing.

+
+
Parameters
+

ediv (int) – number of required elements on this arc

+
+
+
+ +
+
+set_esize(esize)[source]
+

Sets the size of mesh elements on the arc when meshing.

+
+
Parameters
+

esize (float) – size of mesh elements on this arc

+
+
+
+ +
+
+set_pt(ind, point)[source]
+

Update the arc’s point at ind to the new point.

+
+
Parameters
+
    +
  • ind (int) – index of the point we’re updating, 0=start, 1=end

  • +
  • point (Point) – new point we are assigning to Line

  • +
+
+
+
+ +
+
+static sgn(val)[source]
+

Returns sign of the passed value.

+

Helper function for self.interects code.

+
+
Parameters
+

val (float) – passed value

+
+
Returns
+

sign of the passed value, assumes sign of zero = 1

+
+
Return type
+

1 or -1

+
+
+
+ +
+
+signed_copy(sign)[source]
+

Returns a SignArc instance of this Arc with the passed sign.

+
+ +
+
+touches(other)[source]
+

Checks if this arc is connected to a passed line.

+
+
Parameters
+

other (Line or Arc) – passed other line

+
+
Returns
+

beginning or end point connect to other line +False: lines are not connected

+
+
Return type
+

True

+
+
+
+ +
+ +
+
+class pycalculix.geometry.Area(part, line_list=[])[source]
+

Bases: pycalculix.base_classes.Idobj

+

Makes an area.

+

Area is closed in a clockwise direction.

+
+
Parameters
+
    +
  • part (Part) – parent Part

  • +
  • line_list (list) – list of Lines and Arcs that close the area

  • +
+
+
+
+
+part
+

parent part

+
+
Type
+

Part

+
+
+
+ +
+
+closed
+

True if closed, False if not

+
+
Type
+

boolean

+
+
+
+ +
+
+exlines
+

LineLoop of signlines that define the area exterior

+
+
Type
+

LineLoop

+
+
+
+ +
+
+signlines
+

list of signed lines or arcs that define the area

+
+
Type
+

list

+
+
+
+ +
+
+lines
+

a list of all the lines that make the area, includes hole +lines

+
+
Type
+

list

+
+
+
+ +
+
+points
+

a list of all points making the area, excludes arc centers

+
+
Type
+

list

+
+
+
+ +
+
+allpoints
+

a list of all points, includes arc centers

+
+
Type
+

list

+
+
+
+ +
+
+holepoints
+

a list of hole points, excludes arc centers

+
+
Type
+

list

+
+
+
+ +
+
+matl
+

the material fo the area

+
+
Type
+

Matl

+
+
+
+ +
+
+etype
+

element type of area. Options are: +‘plstress’: plane stress +‘plstrain’: plane strain +‘axisym’: axisymmetric

+
+
Type
+

str

+
+
+
+ +
+
+area
+

in-plane part area (ballpark number, arcs not calc right)

+
+
Type
+

double

+
+
+
+ +
+
+holes
+

list of LineLoop of signlines

+
+
Type
+

list

+
+
+
+ +
+
+center
+

the area centroid point. None before closed.

+
+
Type
+

Point or None

+
+
+
+ +
+
+nodes
+

child mesh nodes that are in the area

+
+
Type
+

list

+
+
+
+ +
+
+elements
+

child elements that are in the area

+
+
Type
+

list

+
+
+
+ +
+
+add_hole_sline(signline)[source]
+

Adds signline to the area hole definition.

+

Line is added to the end of the lines list. +Area is closed if needed.

+
+
Returns
+

boolean telling if the hole was closed

+
+
Return type
+

closed (bool)

+
+
+
+ +
+
+add_sline(signline)[source]
+

Adds signline to the area definition.

+

SignLine is added to the end of the lines list. +Area is closed if needed.

+
+ +
+
+property allpoints
+

Returns a list of all points under this area; includes arc centers.

+
+ +
+
+calc_area_center()[source]
+

Calculates and returns the area and centroid Point.

+
+
Returns
+

[area, Point]

+
+
Return type
+

list

+
+
+
+ +
+
+close()[source]
+

Closes the area.

+
+ +
+
+contains_point(point)[source]
+

Returns bool telling if pt is inside the area.

+
+
Parameters
+

pt (Point) – point we are checking

+
+
Returns
+

True if in this area False if not

+
+
Return type
+

contains (bool)

+
+
+
+ +
+
+get_maxlength()[source]
+

Returns max distance between points in an area.

+
+ +
+
+get_name()[source]
+

Returns the area name.

+
+ +
+
+get_patch()[source]
+

Returns a patch of the area.

+
+ +
+
+property holepoints
+

Returns a list of all points in this area’s holes.

+
+ +
+
+label(axis)[source]
+

Labels the area on a Matplotlib axis

+
+
Parameters
+

axis (Matplotlib Axis) – Matplotlib Axis

+
+
+
+ +
+
+line_from_startpt(pt)[source]
+

Returns the signline in the area that start at the passed pt.

+
+
Parameters
+

pt (Point) – the start point of the line one wants

+
+
Returns
+

+
Returns None if no line is found, otherwise the correct line or
+
arc which starts with pt will be
+
returned.
+
+

+
+
Return type
+

match (None or SignLine or SignArc)

+
+
+
+ +
+
+line_insert(lgiven, lnew, after=True)[source]
+

Inserts line lnew before or after the given line.

+
+
Parameters
+
    +
  • lgiven (SignLine or SignArc) – the line we are inserting relative to

  • +
  • lnew (SignLine or SignArc) – the new line we will insert

  • +
  • after (bool) – if True insert lnew after lgiven, false insert before

  • +
+
+
Returns
+

True if succeeded, False if failed

+
+
Return type
+

bool

+
+
+
+ +
+
+property lines
+

Returns a list of all Line and Arc under this area.

+
+ +
+
+plot(axis, label=True, color='yellow')[source]
+

Plots the area on a Matplotlib axis.

+
+
Parameters
+
    +
  • axis (Matplotlib axis) – Matplotlib axis

  • +
  • label (bool) – if True, label area

  • +
+
+
+
+ +
+
+property points
+

Returns a list of all points under this area; excludes arc centers.

+
+ +
+
+rem_sline(sline)[source]
+

Removes a sline from this area.

+
+ +
+
+set_child_ccxtypes()[source]
+

Assigns the child element ccx types.

+
+ +
+
+set_esize(esize)[source]
+

Sets the size of mesh elements on the area when meshing.

+
+
Parameters
+

esize (float) – size of mesh elements on this area

+
+
+
+ +
+
+set_etype(etype)[source]
+

Sets the area etype.

+

Sets the ccxtype on elements if they have been assigned to this area.

+
+
Parameters
+

etype (str) – element type +‘plstress’ plane stress +‘plstrain’: plane strain +‘axisym’: axisymmetric

+
+
+
+ +
+
+property signlines
+

Returns a list of all SignLine and SignArc under this area.

+
+ +
+
+update(line_list)[source]
+

Updates the area’s external lines to the passed list.

+

If the area is cosed, self.close() will be called.

+
+
Parameters
+

line_list (list) – list of SignLine and SignArc items

+
+
+
+ +
+ +
+
+class pycalculix.geometry.Line(start_pt, end_pt)[source]
+

Bases: pycalculix.base_classes.Idobj

+

Stores a line from points start_pt to end_pt.

+
+
Parameters
+
    +
  • start_pt (Point) – first point

  • +
  • end_pt (Point) – second point

  • +
+
+
+
+
+points
+

list of the line’s points [start_pt, end_pt]

+
+
Type
+

list of Point

+
+
+
+ +
+
+ediv
+

number of elements on the line

+
+
Type
+

None or float

+
+
+
+ +
+
+midpt
+

a mid-point between start_pt and end_pt

+
+
Type
+

Point

+
+
+
+ +
+
+nodes
+

a list of meshed nodes on the line

+
+
Type
+

list of Node

+
+
+
+ +
+
+edge
+

True if line is a non-shared edge in an area

+
+
Type
+

bool

+
+
+
+ +
+
+signlines
+

list of SignLine that use this Line

+
+
Type
+

list

+
+
+
+ +
+
+add_signline(signline)[source]
+

Adds a signline to self.signlines.

+
+ +
+
+property allpoints
+

Returns all line defining points.

+
+ +
+
+arc_tang_intersection(point, mag)[source]
+

Returns an intersection point on this line of an arc at center point

+
+
Parameters
+
    +
  • point (Point) – arc center point

  • +
  • mag (float) – passed radius distance from the arc center to the line

  • +
+
+
Returns
+

Point: the intersection point +None: returns None if no intersection point exists

+
+
Return type
+

newpt (Point or None)

+
+
+
+ +
+
+property areas
+

Returns all areas that signlines use.

+
+ +
+
+coincident(point)[source]
+

Checks to see if point is on the line.

+
+
Parameters
+

point (Point) – input point to check

+
+
Returns
+

True if point on line or False if not

+
+
Return type
+

bool

+
+
+
+ +
+
+get_abc()[source]
+

Returns a list of abc terms for a line definition.

+

ax + by + c = 0

+
+
Returns
+

list of the above line terms

+
+
Return type
+

[a, b, c]

+
+
+
+ +
+
+get_name()[source]
+

Returns the line name.

+
+ +
+
+get_perp_vec(point=None)[source]
+

Returns vector perpendicular to current line.

+

Vector is created by rotating the current line ccw 90 degrees +around the start point.

+
+
Parameters
+

point (Point) – defaults to None, unused for Line, used for Arc

+
+
Returns
+

vector that is perpendiculat to the current line

+
+
Return type
+

lvect (Point)

+
+
+
+ +
+
+get_tan_vec(point)[source]
+

Returns vector tangent to the current line.

+

Vector points from the passed point to a unit location further away.

+
+
Parameters
+

point (Point) – start point

+
+
Returns
+

vector that is tangent to the current line

+
+
Return type
+

lvect (Point)

+
+
+
+ +
+
+intersects(other)[source]
+

Checks if other line intersects this line.

+
+
Parameters
+

other (Line, Arc, Signline, SignArc) – other line to check

+
+
Returns
+

Point: intersection point +None: None if lines don’t intersect

+
+
Return type
+

Point or None

+
+
+
+ +
+
+length()[source]
+

Returns the length of the line.

+
+ +
+
+mid()[source]
+

Calculates and returns a midpoint between start and end points.

+
+ +
+
+offset(dist)[source]
+

Returns a new line offset from the dirrent line by value dist.

+

Right hand rule is followed assuming x is the origina line. +Z points out of page. +Y is the direction of line offset.

+
+
Parameters
+

dist (float) – distance to offset the new line by

+
+
Returns
+

new Line which is offset from self by dist

+
+
Return type
+

tmpline (Line)

+
+
+
+ +
+
+pt(ind)[source]
+

Returns the start or end point of the line.

+
+
Parameters
+

ind (int) – index of the point we want, 0=start, 1=end

+
+
Returns
+

requested Point

+
+
Return type
+

Point

+
+
+
+ +
+
+reverse()[source]
+

Reverses self.

+
+ +
+
+save_to_points()[source]
+

This stores this line in the line’s child points.

+
+ +
+
+set_ediv(ediv)[source]
+

Sets the number of element divisions on the line when meshing.

+
+
Parameters
+

ediv (int) – number of required elements on this line

+
+
+
+ +
+
+set_esize(esize)[source]
+

Sets the size of mesh elements on the line when meshing.

+
+
Parameters
+

esize (float) – size of mesh elements on this line

+
+
+
+ +
+
+set_pt(ind, point)[source]
+

Update the line’s point at ind to the new point.

+
+
Parameters
+
    +
  • ind (int) – index of the point we’re updating, 0=start, 1=end

  • +
  • point (Point) – new point we are assigning to Line

  • +
+
+
+
+ +
+
+signed_copy(sign)[source]
+

Returns a SignLine copy of this Line with the passed sign.

+
+ +
+
+touches(other)[source]
+

Checks if a line is connected to a passed line.

+
+
Parameters
+

other (Line or Arc) – passed other line

+
+
Returns
+

beginning or end point connect to other line +False: lines are not connected

+
+
Return type
+

True

+
+
+
+ +
+ +
+
+class pycalculix.geometry.LineLoop(items=[], hole_bool=False, parent=None)[source]
+

Bases: list

+

Makes a LineLoop, which stores multiple Line/Arc or SignLine/SignArc.

+
+
Parameters
+
    +
  • items (list) – Line or Arc or SignLine or SignArc

  • +
  • hole_bool (bool) – True if hole, False otherwise

  • +
  • parent (Area or None) – parent Area, defaults to None

  • +
+
+
+
+
+closed
+

True if closed

+
+
Type
+

bool

+
+
+
+ +
+
+area
+

the loop’s area, can be pos or neg. Pos = cw, neg = ccw

+
+
Type
+

float

+
+
+
+ +
+
+hole
+

True if loop is a hole

+
+
Type
+

bool

+
+
+
+ +
+
+ccw
+

True if loop is counter-clockwise

+
+
Type
+

bool

+
+
+
+ +
+
+center
+

a point at the loop centroid

+
+
Type
+

Point

+
+
+
+ +
+
+parent
+

the loop’s parent Area

+
+
Type
+

Area or None

+
+
+
+ +
+
+append(item)[source]
+

Adds an item to this LineLoop

+
+
Parameters
+

item (SingArc or SignLine) – item to add to the list

+
+
+
+ +
+
+property area
+

Returns the loop’s signed area.

+

Returns 0 if not closed. +Pos = cw, neg = ccw

+

Formula from: http://mathworld.wolfram.com/PolygonArea.html

+
+ +
+
+property ccw
+

Returns bool telling if the area is closed and clockwise.

+
+ +
+
+property center
+

Returns None or a Point at the line loop’s center.

+
+ +
+
+property closed
+

Returns True if Closed, False if not.

+
+ +
+
+contains_point(point)[source]
+

Returns bool telling if pt is inside the LineLoop.

+
+
Parameters
+

pt (Point) – point we are checking

+
+
Returns
+

True if in this area False if not

+
+
Return type
+

contains (bool)

+
+
+
+ +
+
+get_patch()[source]
+

Returns a patch of the LineLoop, or None of not closed.

+
+ +
+
+insert(index, item)[source]
+

Inserts a SignLine or SignArc item into this LineLoop

+
+
Parameters
+

item (SingArc or SignLine) – item to insert

+
+
+
+ +
+
+inside(other)[source]
+

Checks to see if self is inside other.

+

Only checks self’s points.

+
+ +
+
+remove(item)[source]
+

Removes an SignLine or SignArc item from this LineLoop

+
+
Parameters
+

item (SignArc or SignLine) – item to remove

+
+
+
+ +
+
+reverse()[source]
+

Reverse the loop direction cw<–>ccw

+

This modifies the order of lines, and flips each line.

+
+ +
+
+set_id(id)[source]
+

Sets the id number

+
+ +
+
+set_parent(parent)[source]
+

Sets the line loop’s parent part.

+
+ +
+ +
+
+class pycalculix.geometry.Point(x, y, z=0)[source]
+

Bases: pycalculix.base_classes.Idobj

+

Makes a point or vector.

+
+
Parameters
+
    +
  • x (float) – vertical coordinate of point

  • +
  • y (float) – horizontal coordinate of point

  • +
  • z (float) – in-page coordinate of point, defaults to zero

  • +
+
+
+
+
+x
+

vertical coordinate of point

+
+
Type
+

float

+
+
+
+ +
+
+y
+

horizontal coordinate of point

+
+
Type
+

float

+
+
+
+ +
+
+z
+

in-page coordinate of point, defaults to zero +nodes (Node): a list of nodes in a mesh at this point. +List length is 1.

+
+
Type
+

float

+
+
+
+ +
+
+nodes
+

list of nodes under point

+
+
Type
+

list

+
+
+
+ +
+
+lines
+

set of lines which use this point

+
+
Type
+

set

+
+
+
+ +
+
+arc_center
+

True if this point is an arc center

+
+
Type
+

bool

+
+
+
+ +
+
+ang_bet_deg(other)[source]
+

Returns angle between self and other vector in degrees.

+

Assumes vectors start at 0,0

+
+
Parameters
+

other (Point) – passed vector

+
+
Returns
+

degrees between self vector and other vector

+
+
Return type
+

angle (float)

+
+
+
+ +
+
+ang_bet_rad(other)[source]
+

Returns angle between self and other vector in radians.

+

Assumes vectors start at 0,0

+
+
Parameters
+

other (Point) – passed vector

+
+
Returns
+

radians between self vector and other vector

+
+
Return type
+

angle (float)

+
+
+
+ +
+
+ang_deg()[source]
+

Returns angle in degrees, assume point is vector from 0,0.

+
+ +
+
+ang_rad()[source]
+

Returns angle in radians, assume point is vector from 0,0.

+
+ +
+
+axrad()[source]
+

Returns a tuple of (ax, rad) for plotting.

+
+ +
+
+get_name()[source]
+

Returns item name.

+
+ +
+
+label(axis)[source]
+

Labels the point on a Matplotlib axis.

+
+
Parameters
+

axis (Matplotlib Axis) – Matplotlib Axis

+
+
+
+ +
+
+length()[source]
+

Returns the length of this point or vector.

+
+ +
+
+make_unit()[source]
+

Modifies self vector to make it a unit vector.

+
+ +
+
+plot(axis, label=True)[source]
+

Plots the point on the passed matplotlib axis.

+
+
Parameters
+
    +
  • axis (Matplotlib axis) – plate to plot the point

  • +
  • pnum (bool) – True turns on point labeling

  • +
+
+
+
+ +
+
+radax()[source]
+

Returns a tuple of (rad, ax) for checking if inside.

+
+ +
+
+rot_ccw_deg(ang)[source]
+

Rotates the current vector by ccw degrees about 0,0. Returns self.

+
+
Parameters
+

ang (float) – angle or rotation in degrees, counter-clockwise (ccw)

+
+
+
+ +
+
+set_esize(size)[source]
+
+ +
+
+set_line(line)[source]
+

Saves the line or arc in the point.

+
+ +
+
+unset_line(line)[source]
+

Removes the line or arc in the point.

+
+ +
+ +
+
+class pycalculix.geometry.SignArc(parent_line, sign)[source]
+

Bases: pycalculix.geometry.Arc, pycalculix.base_classes.Idobj

+

Makes a signed arc.

+
+
+line
+

parent arc

+
+
Type
+

Arc

+
+
+
+ +
+
+sign
+

1 = positive, or -1 = negative

+
+
Type
+

int

+
+
+
+ +
+
+lineloop
+

parent LineLoop, default is None

+
+
Type
+

None or LineLoop

+
+
+
+ +
+
+nodes
+

nodes on the arc

+
+
Type
+

list

+
+
+
+ +
+
+faces
+

list of element faces

+
+
Type
+

list

+
+
+
+ +
+
+n1
+

list of first order nodes on line

+
+
Type
+

list

+
+
+
+ +
+
+property actr
+

Return the arc center.

+
+ +
+
+property concavity
+

Return the arc’s concavity ‘concave’ or ‘convex’

+
+ +
+
+property edge
+

Returns a bool saying if this is an edge line.

+
+ +
+
+get_name()[source]
+

Returns the line name.

+
+ +
+
+label(ax)[source]
+

Labels the arc on the matplotlib ax axis.

+
+
Parameters
+

ax (Matplotlib axis) – Matplotlib axis

+
+
+
+ +
+
+property midpt
+

Return the mid point.

+
+ +
+
+property nodes
+

Gets the line nodes.

+
+ +
+
+plot(ax, label=True)[source]
+

Draws and labels the arc onto the passed Matplotlib axis.

+
+
Parameters
+
    +
  • ax – Matplotlib axis

  • +
  • label (bool) – if True, label the arc

  • +
+
+
+
+ +
+
+property points
+

Return a list of the arc’s points [pstart, pend]

+
+ +
+
+pt(ind)[source]
+

Returns the point at the start or end of the line.

+
+ +
+
+property radius
+

Return the arc radius.

+
+ +
+
+reverse()[source]
+

Reverses self.line

+
+ +
+
+set_ediv(ediv)[source]
+

Apply the element divisions onto the parent line.

+
+ +
+
+set_esize(esize)[source]
+

Applies the element size onto the parent line.

+
+ +
+
+set_lineloop(lineloop)[source]
+

Sets the parent LineLoop

+
+ +
+
+set_pt(ind, point)[source]
+

Sets the passed point to at the index location.

+
+ +
+
+signed_copy(sign)[source]
+

Returns a SignArc copy of this arc.

+
+ +
+ +
+
+class pycalculix.geometry.SignLine(parent_line, sign)[source]
+

Bases: pycalculix.geometry.Line, pycalculix.base_classes.Idobj

+

Makes a signed line.

+
+
+line
+

parent line

+
+
Type
+

Line

+
+
+
+ +
+
+sign
+

1 = positive, or -1 = negative

+
+
Type
+

int

+
+
+
+ +
+
+lineloop
+

parent LineLoop, default is None

+
+
Type
+

None or LineLoop

+
+
+
+ +
+
+faces
+

list of element faces

+
+
Type
+

list

+
+
+
+ +
+
+n1
+

list of first order nodes on line

+
+
Type
+

list

+
+
+
+ +
+
+nodes
+

nodes on the line

+
+
Type
+

list

+
+
+
+ +
+
+property edge
+

Returns a bool saying if this is an edge line.

+
+ +
+
+get_name()[source]
+

Returns the SignLine name.

+
+ +
+
+label(ax)[source]
+

Labels the line on the matplotlib ax axis.

+
+
Parameters
+

ax (Matplotlib axis) – Matplotlib axis

+
+
+
+ +
+
+property midpt
+

Returns the mid point.

+
+ +
+
+property nodes
+

Gets the line nodes.

+
+ +
+
+plot(ax, label=True)[source]
+

Draws and labels the line onto the passed Matplotlib axis.

+
+
Parameters
+
    +
  • ax (Matlotlib axis) – Matplotlib axis

  • +
  • label (bool) – if True label the line

  • +
+
+
+
+ +
+
+property points
+

Returns a list of the line’s points [pstart, pend]

+
+ +
+
+pt(index)[source]
+

Returns the point at the start or end of the line.

+
+
Parameters
+

index (int) – 0 = start point, 1 = end point

+
+
+
+ +
+
+reverse()[source]
+

Reverses self.line

+
+ +
+
+set_ediv(ediv)[source]
+

Applies the element divisions onto the parent line.

+
+ +
+
+set_esize(esize)[source]
+

Applies the element size onto the parent line.

+
+ +
+
+set_lineloop(lineloop)[source]
+

Sets the parent LineLoop

+
+ +
+
+set_pt(index, point)[source]
+

Sets the start or end point of the line to a new point.

+
+
Parameters
+
    +
  • index (int) – 0 for start point, 1 for end point

  • +
  • point (Point) – the new point we are updating to

  • +
+
+
+
+ +
+
+signed_copy(sign)[source]
+

Returns a SignLine copy of this line.

+
+ +
+ +
+
+pycalculix.geometry.get_text_hv(angle)[source]
+

Returns (ha, va) text horizontal and vertical alignment for line label.

+

This makes the line label text inside its area, assuming areas closed CW.

+
+
Parameters
+

angle (float) – line normal vector in degrees, between 180 and -180

+
+
+
+ +
+
+

pycalculix.installer module

+
+
+pycalculix.installer.add()[source]
+

Installs the fea tools on windows/mac/linux

+
+ +
+ +

Finds all dlls in folder_from and adds hard links to them in folder_to +Or removes the hard links if add=False

+
+ +
+
+pycalculix.installer.find_brew_binary_location(package_folder, search_string)[source]
+

Finds the location of a binary installed by homebrew

+
+ +
+
+pycalculix.installer.get_direct_url(url, headers)[source]
+

Gets the zip direct download link from the project download page

+
+ +
+ +

Returns the url for a link with link_text description, if this +function fails, it raises an error with tells the users a link +which creates an issue on the pycalculix repo

+
+ +
+
+pycalculix.installer.mac_add()[source]
+

Adds ccx and gmsh programs on mac, uses brew

+
+ +
+
+pycalculix.installer.mac_add_ccx()[source]
+
+ +
+
+pycalculix.installer.mac_remove()[source]
+

Removes ccx and gmsh programs on mac, uses brew

+
+ +
+
+pycalculix.installer.printos(os, bits)[source]
+

Prints the operating system and bit size

+
+ +
+
+pycalculix.installer.remove()[source]
+

Removes the fea tools on windows/mac/linux

+
+ +
+
+pycalculix.installer.remove_like(search_path, name)[source]
+

Searches for files or folders matching name in search_path and deletes them

+
+ +
+
+pycalculix.installer.ubuntu_add()[source]
+

Adds ccx and gmsh programs on ubuntu, uses apt

+
+ +
+
+pycalculix.installer.ubuntu_remove()[source]
+

Removes ccx and gmsh programs on ubuntu, uses apt

+
+ +
+
+pycalculix.installer.win_add_ccx(bitsize, binaries_url, program_name)[source]
+

Installs ccx on a windows computer

+
+ +
+
+pycalculix.installer.win_add_gmsh(bitsize, binaries_url, program_name, version_str)[source]
+

Installs a program from an apache server file listing page

+
+ +
+
+pycalculix.installer.windows_add(bitsize)[source]
+

Adds ccx and gmsh programs on windows

+
+ +
+
+pycalculix.installer.windows_remove(bitsize)[source]
+

Removes ccx and gmh programs on windows

+
+ +
+
+pycalculix.installer.zipfile_by_bitsize(binaries_url, headers, zipfile_regex, bitsize)[source]
+

Returns the url linking to the correct zipfile

+
+ +
+
+

pycalculix.loads module

+

This module stores load classes.

+
+
+class pycalculix.loads.ConstLoad(ltype, comp, val)[source]
+

Bases: object

+

Makes a load. Many types are supported.

+
+
Possible load types:
    +
  • forces

  • +
  • displacements

  • +
  • thickness (on nodes)

  • +
  • matl (on elements)

  • +
  • pressure (or stress on faces)

  • +
  • gravity (on elements)

  • +
  • rotation about an axis (on elements)

  • +
+
+
+
+
Parameters
+
    +
  • ltype (string) –

    load type:

    +
      +
    • ’fx’,’fy’,’fz’: force on each axis

    • +
    • ’ux’,’uy’,’uz’: displacement on each axis

    • +
    • ’nodal_thickness’: thickness on element nodes

    • +
    • ’matl’: matl on elements

    • +
    • ’press’: pressure, + is tension, - is compresion

    • +
    • ’gravity’: gravity in x axis direction, - goes towards y axis

    • +
    • ’radps’: radians per second rotation

    • +
    • ’rpm’: rotations per minute rotation

    • +
    +

  • +
  • comp (Component) – component that the load is applied to

  • +
  • val (double or Matl) – value of the load +Matl is needed when setting material loads +otherwise a double value is used to describe the load

  • +
+
+
+
+
+ccx()[source]
+

Writes a load for Calculix ccx solver.

+

Returns a list strings, where each item is a line to write +to a Calculix .inp text file.

+
+ +
+
+get_list()[source]
+

Returns a list of [item, val] for plotting.

+
+ +
+ +
+
+class pycalculix.loads.LinearLoad(ltype, comp, const, mult, xo, axis='x')[source]
+

Bases: object

+

Makes a load which varies depending on x, y, or z location.

+
+
Load Equation:

Load(x) = const + mult*(xo - x)

+
+
+

This load is used to set water pressure, where p = po + rho*g*(xo - x) +For water pressure this becomes:

+
+
    +
  • P(x) = po + (rho*g)*(xo-x)

  • +
  • P(x) = po + mult*(xo-x)

  • +
  • g is positive, xo > x

  • +
+
+
+
Parameters
+
    +
  • ltype (str) – load type ‘press_fluid’ is the only valid option

  • +
  • comp (Component) – component that the load acts on

  • +
  • const (float) – constant term in load equation

  • +
  • mult (float) – mult term in the load equation

  • +
  • xo (float) – xo term in the load equation

  • +
  • axis (str) – axis that the load depends on +‘x’ or ‘y’ are valid options

  • +
+
+
+
+
+ccx()[source]
+

Writes a load for Calculix ccx solver.

+

Returns a list strings, where each item is a line to write +to a Calculix .inp text file.

+
+ +
+
+get_list()[source]
+

Returns a list of [item, val] for plotting.

+
+ +
+
+get_val(xval)[source]
+

Returns the load function value at the given location.

+
+
Parameters
+

xval (float) – the location where we want to find the load

+
+
+
+ +
+ +
+
+

pycalculix.material module

+

This module stores material classes.

+
+
+class pycalculix.material.Material(name)[source]
+

Bases: pycalculix.base_classes.Idobj

+

Makes a linear elastic meterial.

+
+
Parameters
+

name (str) – a unique matl name

+
+
+
+
+- id
+

material id number

+
+
Type
+

int

+
+
+
+ +
+
+- mechtype
+

defines mechanical material type: linear or non-linear material

+
+
Type
+

str

+
+
+
+ +
+
+- density
+

density in mass/volume units

+
+
Type
+

float

+
+
+
+ +
+
+- pratio
+

poisson’s ratio (unitless)

+
+
Type
+

float

+
+
+
+ +
+
+- youngs
+

young’s modulus in Force/area = stress units

+
+
Type
+

float

+
+
+
+ +
+
+- mechtpye
+

mechanical material type +options: ‘linear’ or ‘nonlinear’

+
+
Type
+

str

+
+
+
+ +
+
+- exponent
+

exponent of Ramberg-Osgood stress-strain equation

+
+
Type
+

float

+
+
+
+ +
+
+- yield_stress
+

yield stress of material

+
+
Type
+

float

+
+
+
+ +
+
+- yield_offset
+

yield offset of Ramberg-Osgood stress-strain equation

+
+
Type
+

float

+
+
+
+ +
+
+- conductivity
+

thermal conductivity, Power / (distance-temp)

+
+
Type
+

float

+
+
+
+ +
+
+- spec_heat
+

specific heat (energy/(mass-temp)

+
+
Type
+

float

+
+
+
+ +
+
+- thermal_exp
+

a dict storing temperature dependent thermal +– expansion properties

+

– Thermal expansion is in strain per temperature

+
    +
  • dict[‘data’] = zip of (temp, thermal_expansion)

  • +
  • dict[‘tzero’] = the temperature zero point

  • +
+
+
Type
+

dict

+
+
+
+ +
+
+ccx()[source]
+

Returns a list of text strings for ccx defining the material.

+
+ +
+
+set_mech_props(density, youngs, pratio, mechtype='linear', exponent=0.0, yield_stress=0.0, yield_offset=0.0)[source]
+

Sets the mechanical properties: density, youngs, poisson_ratio.

+
+
Parameters
+
    +
  • density (-) – density in mass/volume units

  • +
  • pratio (-) – poisson’s ratio (unitless)

  • +
  • youngs (-) – young’s modulus in Force/area = stress units

  • +
+
+
+
+
Kargs:
    +
  • mechtpye (str): mechanical material type +options: ‘linear’ or ‘nonlinear’

  • +
  • exponent (float): exponent of Ramberg-Osgood stress-strain equation

  • +
  • yield_stress (float): yield stress of material

  • +
  • yield_offset (float): yield offset of Ramberg-Osgood stress-strain equation

  • +
+
+
+
+ +
+
+set_therm_expan(alphas, temps=None, tzero=None)[source]
+

Sets the thermal expansion of the material.

+
+
Parameters
+
    +
  • alphas (-) – list of thermal expansion alphas +length must be the same as the passed temps

  • +
  • temps (-) – list of temperatures

  • +
  • tzero (-) – temperature zero point

  • +
+
+
+
+ +
+
+set_therm_props(conductivity, spec_heat)[source]
+

Sets the thermal properties: conductivity, specifc_heat.

+
+
Parameters
+
    +
  • conductivity (-) – Power / (distance-temp)

  • +
  • spec_heat (-) – specific heat (energy/(mass-temp)

  • +
+
+
+
+ +
+ +
+
+

pycalculix.mesh module

+

This module stores classes that make a finite element analysis mesh.

+
+
+class pycalculix.mesh.Element(enum, ccxtype, nlist)[source]
+

Bases: object

+

Makes a mesh element.

+

Supports 4 noded quads, 8 noded quads, 3 noded tris, 6 noded tris.

+
+
Parameters
+
    +
  • enum (int) – element id number

  • +
  • ccxtype (str) –

    ccx element type string, 4 characters long

    +

    Description: element_shape + element_order + element_type

    +
      +
    • +
      element_shape = ‘quad’ or ‘tri’
        +
      • ’quad’ = quadrangle

      • +
      • ’tri’ = triangle

      • +
      +
      +
      +
    • +
    • +
      element_order = ‘1’ or ‘2’
        +
      • ’1’ = corner nodes only (3 or 4 noded element)

      • +
      • ’2’ = corner nodes and midside nodes (6 or 8 noded element)

      • +
      +
      +
      +
    • +
    • +
      element_type = ‘plstress’ or ‘plstrain’ or ‘axisym’
        +
      • ’plstress’ = plane stress

      • +
      • ’plstrain’ = plane strain

      • +
      • ’axisym’ = axisymmetric

      • +
      +
      +
      +
    • +
    +
    +
    axisymmetric
      +
    • ’CAX6’ : ‘tri2axisym’

    • +
    • ’CAX3’ : ‘tri1axisym’

    • +
    • ’CAX8’ : ‘quad2axisym’

    • +
    • ’CAX4’ : ‘quad1axisym’

    • +
    +
    +
    plane stress
      +
    • ’CPS6’ : ‘tri2plstress’

    • +
    • ’CPS3’ : ‘tri1plstress’

    • +
    • ’CPS8’ : ‘quad2plstress’

    • +
    • ’CPS4’ : ‘quad1plstress’

    • +
    +
    +
    plane strain
      +
    • ’CPE6’ : ‘tri2plstrain’

    • +
    • ’CPE3’ : ‘tri1plstrain’

    • +
    • ’CPE8’ : ‘quad2plstrain’

    • +
    • ’CPE4’ : ‘quad1plstrain’

    • +
    +
    +
    +

  • +
  • nlist (list) – list of Node, the nodes that define the element

  • +
+
+
+
+
+id
+

element id number

+
+
Type
+

int

+
+
+
+ +
+
+ccxtype
+

ccx element type string, 4 characters long

+

Description: element_shape + element_order + element_type

+
    +
  • +
    element_shape = ‘quad’ or ‘tri’
      +
    • ‘quad’ = quadrangle

    • +
    • ‘tri’ = triangle

    • +
    +
    +
    +
  • +
  • +
    element_order = ‘1’ or ‘2’
      +
    • ‘1’ = corner nodes only (3 or 4 noded element)

    • +
    • ‘2’ = corner nodes and midside nodes (6 or 8 noded element)

    • +
    +
    +
    +
  • +
  • +
    element_type = ‘plstress’ or ‘plstrain’ or ‘axisym’
      +
    • ‘plstress’ = plane stress

    • +
    • ‘plstrain’ = plane strain

    • +
    • ‘axisym’ = axisymmetric

    • +
    +
    +
    +
  • +
+
+
axisym
    +
  • ‘CAX6’ : ‘tri2axisym’

  • +
  • ‘CAX3’ : ‘tri1axisym’

  • +
  • ‘CAX8’ : ‘quad2axisym’

  • +
  • ‘CAX4’ : ‘quad1axisym’

  • +
+
+
plane stress
    +
  • ‘CPS6’ : ‘tri2plstress’

  • +
  • ‘CPS3’ : ‘tri1plstress’

  • +
  • ‘CPS8’ : ‘quad2plstress’

  • +
  • ‘CPS4’ : ‘quad1plstress’

  • +
+
+
plane strain
    +
  • ‘CPE6’ : ‘tri2plstrain’

  • +
  • ‘CPE3’ : ‘tri1plstrain’

  • +
  • ‘CPE8’ : ‘quad2plstrain’

  • +
  • ‘CPE4’ : ‘quad1plstrain’

  • +
+
+
+
+
Type
+

str

+
+
+
+ +
+
+node
+

dictionary of nodes, keys are int >= 1

+
+
Type
+

dict

+
+
+
+ +
+
+face
+

dictionary of faces, keys are int >= 1

+
+
Type
+

dict

+
+
+
+ +
+
+nodes
+

list of element nodes

+
+
Type
+

list

+
+
+
+ +
+
+faces
+

list of element faces

+
+
Type
+

list

+
+
+
+ +
+
+center
+

the center point of the element

+
+
Type
+

Point

+
+
+
+ +
+
+calc_center()[source]
+

Returns the element center as a Point.

+
+ +
+
+ccx()[source]
+

Returns text string defining the element for a ccx input file.

+
+ +
+
+property faces
+

Returns list of element faces.

+
+ +
+
+get_area()[source]
+

Returns the element area.

+
+ +
+
+get_name()[source]
+

Returns the element name based on id number.

+
+ +
+
+get_poly()[source]
+

Returns a polygon of the element.

+
+ +
+
+get_tris()[source]
+

Returns a list of triangles for plotting. Triangles are node ids.

+

CCX elements are closed in a CCW direction relative to xy normal +CCX elements are closed in a CW direction relative to yx mine +—> I have to draw in a clockwise direction, otherwise get error +Triangles are closed in a counter clockwise (CCW) direction to yx mine +http://matplotlib.org/api/tri_api.html#matplotlib.tri.Triangulation

+
+ +
+
+label(ax)[source]
+

Labels the element on the passed matplotlib axis.

+
+ +
+
+property nodes
+

Returns list of element nodes.

+
+ +
+
+set_ccxtype(etype)[source]
+

Sets the ccx element type given an etype string.

+
+
Parameters
+

etype (string) –

element type string

+
    +
  • ’plstress’ = plane stress

  • +
  • ’plstrain’ = plane strain

  • +
  • ’axisym’ = axisymmetric

  • +
+

+
+
+
+ +
+ +
+
+class pycalculix.mesh.Face(fnum, node1, node2, element)[source]
+

Bases: object

+

Makes an element face.

+
+
Parameters
+
    +
  • fnum (int) – element face number

  • +
  • node1 (Node) – first node

  • +
  • node2 (Node) – second node

  • +
  • element (Element) – parent element

  • +
+
+
+
+
+id
+

element face number

+
+
Type
+

int

+
+
+
+ +
+
+nodes
+

list of nodes [node1, node2]

+
+
Type
+

list

+
+
+
+ +
+
+nmid
+

midside node, defaults to None

+
+
Type
+

Node or None

+
+
+
+ +
+
+element
+

parent element

+
+
Type
+

Element

+
+
+
+ +
+
+ext
+

stores if face is external

+
+
Type
+

bool

+
+
+
+ +
+
+get_mnorm()[source]
+

Returns a list: [midpoint, normal_vector]

+
+
Returns
+

face midpoint +norm_vect (Point): face normal vector, it is a unit vector

+
+
Return type
+

midpoint (Point)

+
+
+
+ +
+
+length()[source]
+

Return the length of the face.

+
+ +
+
+set_ext()[source]
+

Sets the ext boolean flag to True. Face is external.

+
+ +
+
+set_nmid(nmid)[source]
+

Sets the face midside node, nmid.

+
+
Parameters
+

nmid (Point) – passed node to set as the face midside node

+
+
+
+ +
+ +
+
+class pycalculix.mesh.Node(node_num, x, y, z)[source]
+

Bases: object

+

Makes a mesh node.

+
+
Parameters
+
    +
  • node_num (int) – node number

  • +
  • x (float) – x-coordinate

  • +
  • y (float) – y-coordinate

  • +
  • z (float) – z-coordinate

  • +
+
+
+
+
+id
+

node number

+
+
Type
+

int

+
+
+
+ +
+
+x
+

x-coordinate

+
+
Type
+

float

+
+
+
+ +
+
+y
+

y-coordinate

+
+
Type
+

float

+
+
+
+ +
+
+z
+

z-coordinate

+
+
Type
+

float

+
+
+
+ +
+
+order
+

1 or 2, 1 if it is a corner node, 2 if it is a midside node

+
+
Type
+

int

+
+
+
+ +
+
+elements
+

a set of elements that contain this node

+
+
Type
+

set

+
+
+
+ +
+
+faces
+

a set of faces that contain this node

+
+
Type
+

set

+
+
+
+ +
+
+add_element(element)[source]
+

Adds an element to this node’s set of elements.

+
+
Parameters
+

element (Element) – element to add to set self.elements

+
+
+
+ +
+
+add_face(face)[source]
+

Adds a face to this node’s set of faces.

+
+
Parameters
+

face (Face) – face to add to set self.faces

+
+
+
+ +
+
+ccx()[source]
+

Returns a string defining the node for a ccx inp file.

+
+ +
+
+get_name()[source]
+

Returns the string node name.

+
+ +
+
+label(ax)[source]
+

Labels the node on the passed Matplotlib axis.

+
+ +
+
+set_order(order)[source]
+

Sets the node order, must be 1 or 2.

+
+
Parameters
+

order (int) – node order, 1 is corner node, 2 is midside node

+
+
+
+ +
+ +
+
+

pycalculix.partmodule module

+

This module stores the Part class. It is used to make 2D parts.

+
+
+class pycalculix.partmodule.Part(feamodel)[source]
+

Bases: pycalculix.base_classes.Idobj

+

This makes a part.

+
+
Parameters
+

parent – parent FeaModel

+
+
+
+
+__fea
+

parent FeaModel

+
+
Type
+

FeaModel

+
+
+
+ +
+
+points
+

list or part points, excludes arc centers

+
+
Type
+

list

+
+
+
+ +
+
+allpoints
+

list or part points, includes arc centers

+
+
Type
+

list

+
+
+
+ +
+
+lines
+

list of all Line and Arc that make the part

+
+
Type
+

list

+
+
+
+ +
+
+signlines
+

list of all SignLine and SignArc that make the part

+
+
Type
+

list

+
+
+
+ +
+
+__cursor
+

location of a cursor drawing the part

+
+
Type
+

Point

+
+
+
+ +
+
+__holemode
+

if True, lines will be added to holes, otherwise, +they’ll be added to areas

+
+
Type
+

bool

+
+
+
+ +
+
+areas
+

list of Area that make up the part

+
+
Type
+

list

+
+
+
+ +
+
+left
+

list the parts leftmost lines, they must be vertical

+
+
Type
+

list

+
+
+
+ +
+
+right
+

list the parts rightmost lines, they must be vertical

+
+
Type
+

list

+
+
+
+ +
+
+top
+

list the parts top lines, they must be horizontal

+
+
Type
+

list

+
+
+
+ +
+
+bottom
+

list the parts bottom lines, they must be horizontal

+
+
Type
+

list

+
+
+
+ +
+
+center
+

the area centroid of the part

+
+
Type
+

Point

+
+
+
+ +
+
+nodes
+

list of part’s nodes

+
+
Type
+

list

+
+
+
+ +
+
+elements
+

list of part’s elements

+
+
Type
+

list

+
+
+
+ +
+
+property allpoints
+

Returns list of part points, includes arc centers.

+
+ +
+
+chunk(mode='both', exclude_convex=True, debug=[0, 0])[source]
+

Chunks all areas in the part.

+
+
Parameters
+
    +
  • mode (str) –

    area chunking mode

    +
      +
    • ’both’: cuts areas using holes and exterior points

    • +
    • ’holes’: cut areas using holes points only

    • +
    • ’ext’: cut areas using exterior points only

    • +
    +

  • +
  • exclude_convex (bool) – If true exclude cutting convex tangent points

  • +
+
+
+
+ +
+
+draw_arc(end_x, end_y, center_x, center_y)[source]
+

Makes an arc and adds it to the part.

+
+
Current point is the first arc point.
+
(end_x, end_y) is the end point
+
(center_x, center_y) is the arc center
+
+
+
Degrees: Traversed angle of arc must be < 180 degrees
+
Radians: Traversed angle of arc must be < Pi
+
+
+
Parameters
+
    +
  • end_x (float) – arc end point x-coordinate

  • +
  • end_y (float) – arc end point y-coordinate

  • +
  • center_x (float) – arc center point x-coordinate

  • +
  • center_y (float) – arc center point y-coordinate

  • +
+
+
Returns
+

[arc, arc_start_point, arc_end_point]

+
+
Return type
+

list

+
+
+
+ +
+
+draw_arc_angle(degrees_ccw, center_x, center_y)[source]
+

Makes an arc and adds it to the part.

+
+
Current point is the first arc point.
+
degrees_ccw is the swept angle in degrees, counterclockwise
+
(center_x, center_y) is the arc center
+
+
+
Degrees: Traversed angle of arc must be < 180 degrees
+
+
+
Parameters
+
    +
  • degrees_ccw (float) – arc swept angle in degrees, counterclockwise

  • +
  • center_x (float) – arc center point x-coordinate

  • +
  • center_y (float) – arc center point y-coordinate

  • +
+
+
Returns
+

[arc, arc_start_point, arc_end_point]

+
+
Return type
+

list

+
+
+
+ +
+
+draw_circle(center_x, center_y, radius, num_arcs=4)[source]
+

Draws a circle area and adds it to the part.

+
+
Parameters
+
    +
  • center_x (float) – x-axis hole center

  • +
  • center_y (float) – y-axis hole center

  • +
  • radius (float) – hole radius

  • +
  • num_arcs (int) – number of arcs to use, must be >= 3

  • +
+
+
Returns
+

a LineLoop list of SignArc

+
+
Return type
+

loop (geometry.LineLoop)

+
+
+
+ +
+
+draw_hole(center_x, center_y, radius, num_arcs=4, filled=False)[source]
+

Makes a hole in the part.

+
+
Parameters
+
    +
  • center_x (float) – x-axis hole center

  • +
  • center_y (float) – y-axis hole center

  • +
  • radius (float) – hole radius

  • +
  • num_arcs (int) – number of arcs to use, must be >= 3

  • +
  • filled (bool) –

    whether to fill the hole

    +
      +
    • True: makes a new area in the part

    • +
    +

  • +
+
+
Returns
+

list of hole SignLine or SignArc

+
+
    +
  • Returns None if hole was not made.

  • +
+
+

+
+
Return type
+

hole_lines (list or None)

+
+
+
+ +
+
+draw_line_ax(dy_ax)[source]
+

Draws a line a relative axial distance, and adds it to the part.

+
+
Parameters
+

dy_ax (float) – y-axis delta distance to draw the line

+
+
Returns
+

[line, point_start, point_end]

+
+
Return type
+

list

+
+
+
+ +
+
+draw_line_delta(delta_x, delta_y)[source]
+

Draws a line a relative distance, and adds it to the part.

+
+
Parameters
+
    +
  • delta_x (float) – x-axis delta distance to draw the line

  • +
  • delta_y (float) – y-axis delta distance to draw the line

  • +
+
+
Returns
+

[line, point_start, point_end]

+
+
Return type
+

list

+
+
+
+ +
+
+draw_line_rad(dx_rad)[source]
+

Draws a line a relative radial distance, and adds it to the part.

+
+
Parameters
+

dx_rad (float) – x-axis delta distance to draw the line

+
+
Returns
+

[line, point_start, point_end]

+
+
Return type
+

list

+
+
+
+ +
+
+draw_line_to(x, y)[source]
+

Draws a line to the given location, and adds it to the part.

+
+
Parameters
+
    +
  • x (float) – x-axis coordinate of the end point

  • +
  • y (float) – y-axis coordinate of the end point

  • +
+
+
Returns
+

[SignLine, point_start, point_end]

+
+
Return type
+

list

+
+
+
+ +
+
+fillet_all(radius)[source]
+

Fillets all external lines not within 10 degrees of tangency

+
+
Parameters
+

radius (float) – the fillet radius to use

+
+
Returns
+

list of SignArc

+
+
Return type
+

arcs (list)

+
+
+
+ +
+
+fillet_lines(line1, line2, radius)[source]
+

Fillets the given lines in the part.

+

This inserts an arc in the part tangent to the two given lines.

+
+
Parameters
+
    +
  • line1 (SignLine) – line that the arc starts on, arc is tangent

  • +
  • line2 (SignLine) – line that the arc ends on, arc is tangent

  • +
  • radius (float) – arc radius size

  • +
+
+
Returns
+

[arc, start_point, end_point]

+
+
Return type
+

list

+
+
+
+ +
+
+get_item(item)[source]
+

“Returns the part’s item(s) requested by the passed string.

+
+
Parameters
+

item (str) –

string requesting item(s)

+
    +
  • Valid examples: ‘P0’, ‘L0’, ‘left’, ‘A0’

  • +
+

+
+
Returns
+

If items are found they are returned

+
+
    +
  • If there is only one item it is returned

  • +
  • If there are multiple items, they are returned as a list

  • +
  • If no items are found None is returned

  • +
+
+

+
+
Return type
+

item(s) or None

+
+
+
+ +
+
+get_name()[source]
+

Returns the part name based on id number.

+
+ +
+
+goto(x, y, holemode=False)[source]
+

Moves the part cursor to a location.

+

If that location has a point at it, use it. +If not, make a new point at that location.

+
+
Parameters
+
    +
  • x (float) – x-coordinate of the point to go to

  • +
  • y (float) – y-coordinate of the point to go to

  • +
  • holemode (bool) – if True, we start drawing a hole here, otherwise +we start drawing an area

  • +
+
+
Returns
+

returns the updated cursor point

+
+
Return type
+

self.__cursor (Point)

+
+
+
+ +
+
+label(axis)[source]
+

Labels the part on a Matplotlib axis

+
+
Parameters
+

axis (Matplotlib Axis) – Matplotlib Axis

+
+
+
+ +
+
+property lines
+

Returns list of part lines.

+
+ +
+
+plot(axis, label=True, color='yellow')[source]
+

Plots the part on the passed Matplotlib axis.

+
+
Parameters
+
    +
  • axis (Matplotlib axis) – the axis we will plot on

  • +
  • label (bool) – True displays the part label

  • +
  • color (tuple) – r,g,b,a matplotlib color tuple

  • +
+
+
+
+ +
+
+property points
+

Returns list of part points, excludes arc centers.

+
+ +
+
+property signlines
+

Returns list of part signline and signarc.

+
+ +
+ +
+
+

pycalculix.problem module

+

This module stores the Problem class which is used to solve different types +of analysis.

+
+
+class pycalculix.problem.Problem(feamodel, problem_type, fname='')[source]
+

Bases: pycalculix.base_classes.Idobj

+

Makes a problem which can be analyzed with Calculix ccx.

+
+
Parameters
+
    +
  • feamodel (-) – the parent FeaModel

  • +
  • problem_type (-) – model type, options: +– ‘struct’: structural

  • +
  • fname (-) – file prefix for the problem .inp and results files +If value is ‘’ it will default to the project name of the FeaModel

  • +
+
+
+
+
+fea
+

parent FeaModel

+
+
Type
+

FeaModel

+
+
+
+ +
+
+__ptype
+

problem type, options:

+
    +
  • ‘struct’: structural

  • +
+
+
Type
+

str

+
+
+
+ +
+
+solved
+

boolean storing whether or not the analysis was solved

+
+
Type
+

bool

+
+
+
+ +
+
+fname
+

the problem database and results file prefix

+
+
Type
+

str

+
+
+
+ +
+
+rfile
+

Results_File is loaded in after the model has been solved

+
+
Type
+

Results_File

+
+
+
+ +
+
+solve()[source]
+

Solves the model in Calculix ccx.

+
+ +
+ +
+
+

pycalculix.results_file module

+

This module stores the Results_File class.

+
+
+class pycalculix.results_file.ResultsFile(problem)[source]
+

Bases: object

+

Makes a results file.

+
+
Parameters
+

problem (Problem) – problem that was solved

+
+
+
+
+__problem
+

parent problem

+
+
Type
+

Problem

+
+
+
+ +
+
+__steps
+

a list of float time steps

+
+
Type
+

list

+
+
+
+ +
+
+__results
+

a dict storing the results data +results[step][‘node’][nnum][field] –> value +results[step][‘element’][enum][‘avg’][field] –> value +results[step][‘element’][enum][‘max’][field] –> value +results[step][‘element’][enum][‘min’][field] –> value +results[step][‘element’][enum][‘ipoints’][ipnum][field] –> value +field = ‘ux’ or ‘Seqv’ or ‘ey’ etc.

+
+
Type
+

dict

+
+
+
+ +
+
+__time
+

current time we are looking at, defaults to -1 +When a file is loaded in, the first time step is loaded.

+
+
Type
+

float

+
+
+
+ +
+
+check_ccx_version(timeout=1)[source]
+

Raises an exception of the calculix ccx version is too old

+
+ +
+
+eplot(field, fname='', display=True, levels=21, gmult=1.0, mode='avg', max_val=None, min_val=None, title='')[source]
+

Plots element results.

+
+
Parameters
+
    +
  • field (str) – results item to plot. Only stresses supported. +Examples: ‘Sx’, ‘Sxy’, ‘S1’, ‘Seqv’ etc.

  • +
  • fname (str) – prefix of png file name, if writing an image

  • +
  • display (bool) – True = interactively show the plot

  • +
  • levels (int) – number of levels to use in the colorbar

  • +
  • gmult (int) – geometric multiplier on displacement of nodes +displayed_node_loc = model_node_loc + gmult*node_displacement

  • +
  • mode (str) –

    the type of element result to plot

    +
      +
    • ’avg’: integration points averaged to avg element result

    • +
    • ’max’: max value of field in the integration points plotted

    • +
    • ’min’: min value of field in the integration points plotted

    • +
    +

  • +
  • max_val (float or None) –

    max value in the colorbar

    +
      +
    • None: max from selected data used

    • +
    • float: use the passed float

    • +
    +

  • +
  • min_val (float) –

    min value in the colorbar

    +
      +
    • None: min from selected data used

    • +
    • float: use the passed float

    • +
    +

  • +
  • title (str) – third line in the plot title

  • +
+
+
+
+ +
+
+get_emax(field, time=None, mode='avg')[source]
+

Returns the max results field value of selected elements at curent time.

+
+
Parameters
+
    +
  • field (str) – results field, stresses supported ‘S1’, ‘Sx’, etc.

  • +
  • time (None or float) –

    the time to query

    +
      +
    • None: uses the current time

    • +
    • float: uses the passed float time

    • +
    +

  • +
  • mode (str) –

    type of element result to give back

    +
      +
    • +
      ’max’: for each element only use the max value of field over

      all of its integration points

      +
      +
      +
    • +
    • +
      ’min’: for each element only use the min value of field over

      all of its integration points

      +
      +
      +
    • +
    • +
      ’avg’: for each element only use an average of all integration

      points in the eleemnt. Principal streses and Seqv are +calculated after averaging 6 stress components.

      +
      +
      +
    • +
    +

  • +
+
+
Returns
+

max value

+
+
Return type
+

res (float)

+
+
+
+ +
+
+get_emin(field, time=None, mode='avg')[source]
+

Returns the min results field value of selected elements at curent time.

+
+
Parameters
+
    +
  • field (str) – results field, stresses supported ‘S1’, ‘Sx’, etc.

  • +
  • time (None or float) –

    the time to query

    +
      +
    • None: uses the current time

    • +
    • float: uses the passed float time

    • +
    +

  • +
  • mode (str) –

    type of element result to give back

    +
      +
    • +
      ’max’: for each element only use the max value of field over

      all of its integration points

      +
      +
      +
    • +
    • +
      ’min’: for each element only use the min value of field over

      all of its integration points

      +
      +
      +
    • +
    • +
      ’avg’: for each element only use an average of all integration

      points in the eleemnt. Principal streses and Seqv are +calculated after averaging 6 stress components.

      +
      +
      +
    • +
    +

  • +
+
+
Returns
+

min value

+
+
Return type
+

res (float)

+
+
+
+ +
+
+get_eval(element, field, time=None, mode='avg')[source]
+

Returns the field result value under element.

+

Result will be returned whether or not passed element is selected.

+
+
Parameters
+
    +
  • element (str or Element) – element we are asking about

  • +
  • field (str) – the results item we want: ‘Sy’, ‘Seqv’

  • +
  • mode (str) –

    the type of element result to get

    +
      +
    • ’avg’: integration points averaged to avg element result

    • +
    • ’max’: max value of field in the integration points plotted

    • +
    • ’min’: min value of field in the integration points plotted

    • +
    +

  • +
+
+
Returns
+

float value if field exists, None otherwise

+
+
Return type
+

res (float or None)

+
+
+
+ +
+
+get_fsum(item)[source]
+

Returns the force sum on nodes under a given point or line.

+

Reports results for the current time.

+
+
Parameters
+

item (Point or SignLine) – item that has reaction forces on its nodes

+
+
Returns
+

[fx, fy, fz] reaction forces in each axis, force units

+
+
Return type
+

list

+
+
+
+ +
+
+get_nmax(field, time=None)[source]
+

Returns the max value of node results field in selected nodes.

+

Reports results for the current time.

+
+
Parameters
+
    +
  • field (str) – results field, for example ‘ux’, ‘ey’, ‘S1’, ‘Seqv’

  • +
  • time (None or float) –

    the time to query

    +
      +
    • None: uses the current time

    • +
    • float: uses the passed float time

    • +
    +

  • +
+
+
Returns
+

max value

+
+
Return type
+

res (float)

+
+
+
+ +
+
+get_nmin(field, time=None)[source]
+

Returns the min value of node results field in selected nodes.

+

Reports results for the current time.

+
+
Parameters
+
    +
  • field (str) – results field, for example ‘ux’, ‘ey’, ‘S1’, ‘Seqv’

  • +
  • time (None or float) –

    the time to query

    +
      +
    • None: uses the current time

    • +
    • float: uses the passed float time

    • +
    +

  • +
+
+
Returns
+

min value

+
+
Return type
+

res (float)

+
+
+
+ +
+
+get_nval(node, field, time=None)[source]
+

Returns the field result value under node.

+

Result will be returned whether or not passed node is selected.

+
+
Parameters
+
    +
  • node (str or Node) – node we are asking about

  • +
  • field (str) – the results item we want: ‘ux’, ‘Sy’, ‘Seqv’, ‘fx’

  • +
  • time (None or float) –

    the time to query

    +
      +
    • None: uses the current time

    • +
    • float: uses the passed float time

    • +
    +

  • +
+
+
Returns
+

float value if field exists, None otherwise

+
+
Return type
+

res (float or None)

+
+
+
+ +
+
+get_relative_gradient(start_point, end_point, field, n_poly=3, n_subpoints=500)[source]
+

Calculte relative stress gradient (gradient/start_value)

+
+
Parameters
+
    +
  • [ (end_point) – starting point of line. [x, y]

  • +
  • [ – end point of line. Example: [x, y]

  • +
  • field (str) – results item to plot, examples: ‘ux’, ‘ey’, ‘Seqv’

  • +
+
+
+
+
Kargs:

n_poly (int): numbers of polygons for fitting, min=2 +n_subpoints (int): numbers of points the line is subdivided into

+
+
+
+ +
+
+load()[source]
+

Loads the results file with problem.fname prefix.

+
+ +
+
+nplot(field, fname='', display=True, levels=21, gradient=False, gmult=1.0, max_val=None, min_val=None, title='')[source]
+

Plots nodal results.

+
+
Parameters
+
    +
  • field (str) – results item to plot, examples: ‘ux’, ‘ey’, ‘Seqv’

  • +
  • fname (str) – prefix of png file name, if writing an image

  • +
  • display (bool) – True = interactively show the plot

  • +
  • levels (int) – number of levels to use in the colorbar

  • +
  • gradient (bool) – True = results plotted with gradient +False = results plotted with filled areas

  • +
  • gmult (int) – geometric multiplier on displacement of nodes +displayed_node_loc = model_node_loc + gmult*node_displacement

  • +
  • max_val (float or None) –

    max value in the colorbar

    +
      +
    • None: max from selected data used

    • +
    • float: use the passed float

    • +
    +

  • +
  • min_val (float) –

    min value in the colorbar

    +
      +
    • None: min from selected data used

    • +
    • float: use the passed float

    • +
    +

  • +
  • title (str) – third line in the plot title

  • +
+
+
+
+ +
+
+plot_gradient(start_point, end_point, field, fname='', display=True, title='', max_val=None, min_val=None, curve_fitting=True, n_poly=3, n_subpoints=500, legend=True)[source]
+

Create diagram with data projected onto line on the undeformed geometry.

+
+
Parameters
+
    +
  • [float, float] (end_point) – starting point of line. [x, y]

  • +
  • [float, float] – end point of line. Example: [x, y]

  • +
  • field (str) – results item to plot, examples: ‘ux’, ‘ey’, ‘Seqv’

  • +
  • fname (str) – prefix of png file name, if writing an image

  • +
  • display (bool) – True = interactively show the plot

  • +
  • title (str) – third line in the plot title

  • +
  • max_val (float or None) – max value in the y-axis +- None: max from selected data used +- float: use the passed float

  • +
  • min_val (float or None) – min value in the y-axis +- None: min from selected data used +- float: use the passed float

  • +
  • curve_fitting (bool) – True = a curve is fitted to the gradient

  • +
  • n_poly (int) – numbers of polygons for fitting

  • +
  • n_subpoints (int) – numbers of points the line is subdivided into

  • +
  • legend (bool) – True = legend with fitted equation is shown

  • +
+
+
+
+ +
+
+set_time(time)[source]
+

Sets the time point we’re looking at in the results file.

+
+
Parameters
+

time (float) – time we are setting

+
+
+
+ +
+
+property steps
+

Returns a list of loaded time steps.

+

Note: this is read only, you can not assign a value to it.

+
+ +
+
+property time
+

Returns the current time (float) in the results file.

+

Note: this is read only, you can not assign a value to it.

+
+ +
+ +
+
+

pycalculix.selector module

+

This module stores the Selector object

+

Selector is used in FeaModel to store the model’s selected set.

+
+
+class pycalculix.selector.Selector(feamodel)[source]
+

Bases: object

+

Makes a selector which stores the selected items in the FeaModel.

+
+
Parameters
+

feamodel (FeaModel) – the parent FeaModel object

+
+
+
+
+__fea
+

the parent FeaModel

+
+
Type
+

FeaModel

+
+
+
+ +
+
+__all
+

If everything is selected all=True

+
+
Type
+

bool

+
+
+
+ +
+
+parts
+

currently selected parts

+
+
Type
+

set

+
+
+
+ +
+
+areas
+

currently selected areas

+
+
Type
+

set

+
+
+
+ +
+
+lines
+

currently selected signed lines or arcs

+
+
Type
+

set

+
+
+
+ +
+
+points
+

currently selected points

+
+
Type
+

set

+
+
+
+ +
+
+elements
+

currently selected elements

+
+
Type
+

set

+
+
+
+ +
+
+faces
+

currently selected faces

+
+
Type
+

set

+
+
+
+ +
+
+nodes
+

currently selected nodes

+
+
Type
+

set

+
+
+
+ +
+
+__parts
+

internal storage set of parts

+
+
Type
+

set

+
+
+
+ +
+
+__areas
+

internal storage set of areas

+
+
Type
+

set

+
+
+
+ +
+
+__slines
+

internal storage set of signed lines and arcs

+
+
Type
+

set

+
+
+
+ +
+
+__points
+

internal storage set of points

+
+
Type
+

set

+
+
+
+ +
+
+__elements
+

internal storage set of elements

+
+
Type
+

set

+
+
+
+ +
+
+__faces
+

internal storage set of faces

+
+
Type
+

set

+
+
+
+ +
+
+__nodes
+

internal storage set of nodes

+
+
Type
+

set

+
+
+
+ +
+
+allsel_under(sel_type, full=True, byfaces=False)[source]
+

Selects all progeny under current sel_type items.

+
+
Parameters
+
    +
  • sel_type (str) –

    the item type to select under

    +
      +
    • ’parts’

    • +
    • ’areas’

    • +
    • ’lines’

    • +
    • ’points’ (needs full=True)

    • +
    • ’elements’

    • +
    • ’faces’

    • +
    +

  • +
  • full (bool) –

    if False selects under one branch

    +
      +
    • solid modeling branch: area->line->keypoints->points

    • +
    • mesh branch: elements->faces->nodes

    • +
    +

    Cross branch connections: +- faces are under lines +- elements are under areas +- nodes are under lines and points

    +

    If True, selects all items connected to sub items: +- Parts:

    +
    +
      +
    • Areas

    • +
    • Elements, element faces + element nodes

    • +
    • Lines, line points

    • +
    +
    +
      +
    • Areas:

      +
      +
        +
      • Elements, element faces + element nodes

      • +
      • Lines, line points

      • +
      +
      +
    • +
    +

    -Lines:

    +
    +
      +
    • Faces, nodes

    • +
    • byfaces=True: elements above faces

    • +
    • byfaces=False: elements above line nodes selected

    • +
    +
    +

    -Points:

    +
    +
      +
    • Nodes

    • +
    • byfaces=True: no elements selected

    • +
    • byfaces=False: elements above point nodes selected

    • +
    +
    +

  • +
  • byfaces (bool) –

    If true, the child elements will only be selected +if a parent face was selected. If false just an element node +needs to be selected to select the element.

    +

    -This is only applicable when sel_type == ‘lines’

    +

  • +
+
+
+
+ +
+
+allselect()[source]
+

Selects all items.

+
+ +
+
+property areas
+

Returns selected areas.

+
+ +
+
+deselect(items)[source]
+

Removes the passed item or items from the selection set.

+
+ +
+
+deselect_all(sel_type)[source]
+

Deselects all items of sel_type.

+

All items currently selected items are removed from the selection set +before the all set is selected.

+
+
Parameters
+

sel_type (str) –

the items to select

+
    +
  • ’all’

  • +
  • ’parts’

  • +
  • ’areas’

  • +
  • ’lines’

  • +
  • ’points’

  • +
  • ’elements’

  • +
  • ’faces’

  • +
  • ’nodes’

  • +
+

+
+
+
+ +
+
+property elements
+

Returns selected elements.

+
+ +
+
+property faces
+

Returns selected faces.

+
+ +
+
+property lines
+

Returns selected signed lines or arcs.

+
+ +
+
+property nodes
+

Returns selected nodes.

+
+ +
+
+property parts
+

Returns selected parts.

+
+ +
+
+property points
+

Returns selected points.

+
+ +
+
+print_summary()[source]
+

Prints a summary of the number so items selected in each sel_type.

+
+ +
+
+select(items='', also=False)[source]
+

Selects an item or a list of items.

+
+
Agrs:

items (str or item or list): item(s) to select +also (bool): whether to add the items to the current selection set

+
+
    +
  • True: add the items to the current selection set

  • +
  • False: only select the passed items

  • +
+
+
+
+
+ +
+
+select_above(inclusive=False)[source]
+

Selects parents above currently selected items.

+

Only one set must be selected.

+
+ +
+
+select_above_all(sel_type, inclusive=False)[source]
+

Selects parents above all currently selected items of sel_type.

+
+
Parameters
+
    +
  • sel_type (str) –

    type to select items above

    +
      +
    • ’areas’: selects parts

    • +
    • ’lines’: selects areas

    • +
    • ’points’: selects signlines or signarcs

    • +
    • ’faces’: selects elements

    • +
    • ’nodes’: selects faces

    • +
    +

  • +
  • inclusive (bool) –

    if True, all child entities must be selected for +the parent to be selected. If false all parents connected to +the child are selected. Exampes:

    +
      +
    • ’areas’ + True: all part areas must be selected to select part

    • +
    • ’areas’ + False: parts with selected areas in them are selected

    • +
    • ’lines’ + True: all lines must be selected to select area

    • +
    • ’lines’ + False: areas with selected lines in them are selected

    • +
    +

  • +
+
+
+
+ +
+
+select_all(sel_type='all', also=False)[source]
+

Selects all items of sel_type from the full feamodel.

+

All items currently selected items are removed from the selection set +before the all set is selected.

+
+
Parameters
+
    +
  • sel_type (str) –

    the items to select

    +
      +
    • ’all’

    • +
    • ’parts’

    • +
    • ’areas’

    • +
    • ’lines’

    • +
    • ’points’

    • +
    • ’elements’

    • +
    • ’faces’

    • +
    • ’nodes’

    • +
    +

  • +
  • also (bool) – if False, empties the selection set before selecting +all items, if True adds new items to the current set

  • +
+
+
+
+ +
+
+select_below()[source]
+

Selects children below currently selected items.

+

Only one set must be selected.

+
+ +
+
+select_below_all(sel_type)[source]
+

Selects children below all currently selected items of sel_type.

+
+
Parameters
+

sel_type (str) –

type to select items below

+
    +
  • ’parts’: selects areas

  • +
  • ’areas’: selects lines (type SignLine and SignArc)

  • +
  • ’lines’: selects points

  • +
  • ’elements’: selects faces

  • +
  • ’faces’: selects nodes

  • +
+

+
+
+
+ +
+
+select_none()[source]
+

Empties out the selection object.

+
+ +
+ +
+
+

pycalculix.version module

+
+
+

Module contents

+

pycalculix Description: +This module provides a python interface to the Calculix FEA preprocessor and +solver. Simplicity is emphasized.

+

2D models are supported including: +plane stress, plane strain, and axisymmetric problems

+

Library Capabilities: +Build Geometry +Mesh Geometry +Apply Loads +Solve Model +View Results +Query Results +Select Sub-Portion of Model and view those results

+

See documentation and examples at: +http://justinablack.com/pycalculix/ +https://github.com/spacether/pycalculix

+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/search.html b/dist/documentation/search.html new file mode 100644 index 0000000..2953f5a --- /dev/null +++ b/dist/documentation/search.html @@ -0,0 +1,115 @@ + + + + + + + Search — pycalculix 1.1.4 documentation + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Search

+
+ +

+ Please activate JavaScript to enable the search + functionality. +

+
+

+ From here you can search these documents. Enter your search + words into the box below and click "search". Note that the search + function will automatically search for all of the words. Pages + containing fewer words won't appear in the result list. +

+
+ + + +
+ +
+ +
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/dist/documentation/searchindex.js b/dist/documentation/searchindex.js new file mode 100644 index 0000000..02a0b1b --- /dev/null +++ b/dist/documentation/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({docnames:["index","pycalculix"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":2,"sphinx.ext.viewcode":1,sphinx:56},filenames:["index.rst","pycalculix.rst"],objects:{"":{pycalculix:[1,0,0,"-"]},"pycalculix.base_classes":{FIELDTYPE:[1,1,1,""],Idobj:[1,2,1,""],Itemlist:[1,2,1,""],Meshlist:[1,2,1,""],RESFILEDS:[1,1,1,""],chunk_list:[1,4,1,""],listify:[1,4,1,""],plot_finish:[1,4,1,""],plot_set_bounds:[1,4,1,""]},"pycalculix.base_classes.Idobj":{id:[1,1,1,""],set_id:[1,3,1,""]},"pycalculix.base_classes.Itemlist":{append:[1,3,1,""],get_ids:[1,3,1,""],get_next_id:[1,3,1,""]},"pycalculix.base_classes.Meshlist":{get_maxid:[1,3,1,""],get_minid:[1,3,1,""],idget:[1,3,1,""],set_minid:[1,3,1,""]},"pycalculix.cadimporter":{CadImporter:[1,2,1,""]},"pycalculix.cadimporter.CadImporter":{__fea:[1,1,1,""],__fname:[1,1,1,""],__layer:[1,1,1,""],__swapxy:[1,1,1,""],load:[1,3,1,""]},"pycalculix.components":{Component:[1,2,1,""]},"pycalculix.components.Component":{ccx:[1,3,1,""],get_children:[1,3,1,""],get_name:[1,3,1,""],set_name:[1,3,1,""],write_cgx:[1,3,1,""],write_gmsh:[1,3,1,""]},"pycalculix.connectors":{Contact:[1,2,1,""],SurfaceInteraction:[1,2,1,""]},"pycalculix.connectors.Contact":{ccx:[1,3,1,""],name:[1,3,1,""]},"pycalculix.connectors.SurfaceInteraction":{ccx:[1,3,1,""],name:[1,3,1,""]},"pycalculix.environment":{CCX:[1,1,1,""],CGX:[1,1,1,""],DPI:[1,1,1,""],GMSH:[1,1,1,""],OSVERSIONINFOEXW:[1,2,1,""],get_dpi:[1,4,1,""],get_paths:[1,4,1,""],get_version:[1,4,1,""]},"pycalculix.environment.OSVERSIONINFOEXW":{dwBuildNumber:[1,1,1,""],dwMajorVersion:[1,1,1,""],dwMinorVersion:[1,1,1,""],dwOSVersionInfoSize:[1,1,1,""],dwPlatformId:[1,1,1,""],szCSDVersion:[1,1,1,""],wProductType:[1,1,1,""],wReserved:[1,1,1,""],wServicePackMajor:[1,1,1,""],wServicePackMinor:[1,1,1,""],wSuiteMask:[1,1,1,""]},"pycalculix.feamodel":{FeaModel:[1,2,1,""]},"pycalculix.feamodel.FeaModel":{areas:[1,1,1,""],components:[1,1,1,""],contacts:[1,1,1,""],elements:[1,1,1,""],eorder:[1,1,1,""],eshape:[1,1,1,""],faces:[1,1,1,""],fname:[1,1,1,""],get_item:[1,3,1,""],get_items:[1,3,1,""],get_units:[1,3,1,""],lineloops:[1,1,1,""],lines:[1,1,1,""],loads:[1,1,1,""],make_matl:[1,3,1,""],make_part:[1,3,1,""],make_problem:[1,3,1,""],matls:[1,1,1,""],mesh:[1,3,1,""],nodes:[1,1,1,""],parts:[1,1,1,""],plot_areas:[1,3,1,""],plot_constraints:[1,3,1,""],plot_elements:[1,3,1,""],plot_geometry:[1,3,1,""],plot_lines:[1,3,1,""],plot_multiple:[1,3,1,""],plot_nodes:[1,3,1,""],plot_parts:[1,3,1,""],plot_points:[1,3,1,""],plot_pressures:[1,3,1,""],points:[1,1,1,""],print_summary:[1,3,1,""],problems:[1,1,1,""],register:[1,3,1,""],scale:[1,3,1,""],set_constr:[1,3,1,""],set_contact_linear:[1,3,1,""],set_ediv:[1,3,1,""],set_eshape:[1,3,1,""],set_esize:[1,3,1,""],set_etype:[1,3,1,""],set_fluid_press:[1,3,1,""],set_gravity:[1,3,1,""],set_load:[1,3,1,""],set_matl:[1,3,1,""],set_radps:[1,3,1,""],set_rpm:[1,3,1,""],set_time:[1,3,1,""],set_units:[1,3,1,""],signlines:[1,1,1,""],surfints:[1,1,1,""],time:[1,1,1,""],units:[1,1,1,""],view:[1,1,1,""]},"pycalculix.geometry":{Arc:[1,2,1,""],Area:[1,2,1,""],Line:[1,2,1,""],LineLoop:[1,2,1,""],Point:[1,2,1,""],SignArc:[1,2,1,""],SignLine:[1,2,1,""],get_text_hv:[1,4,1,""]},"pycalculix.geometry.Arc":{actr:[1,1,1,""],add_signline:[1,3,1,""],allpoints:[1,1,1,""],areas:[1,3,1,""],coincident:[1,3,1,""],concavity:[1,1,1,""],edge:[1,1,1,""],ediv:[1,1,1,""],get_ang:[1,3,1,""],get_ang_rad:[1,3,1,""],get_concavity:[1,3,1,""],get_name:[1,3,1,""],get_perp_vec:[1,3,1,""],get_pt_at:[1,3,1,""],get_tan_vec:[1,3,1,""],get_verts_codes:[1,3,1,""],intersects:[1,3,1,""],length:[1,3,1,""],mid:[1,3,1,""],midpt:[1,1,1,""],nodes:[1,1,1,""],points:[1,1,1,""],pt:[1,3,1,""],radius:[1,1,1,""],reverse:[1,3,1,""],save_to_points:[1,3,1,""],set_ediv:[1,3,1,""],set_esize:[1,3,1,""],set_pt:[1,3,1,""],sgn:[1,3,1,""],signed_copy:[1,3,1,""],signlines:[1,1,1,""],touches:[1,3,1,""]},"pycalculix.geometry.Area":{add_hole_sline:[1,3,1,""],add_sline:[1,3,1,""],allpoints:[1,1,1,""],area:[1,1,1,""],calc_area_center:[1,3,1,""],center:[1,1,1,""],close:[1,3,1,""],closed:[1,1,1,""],contains_point:[1,3,1,""],elements:[1,1,1,""],etype:[1,1,1,""],exlines:[1,1,1,""],get_maxlength:[1,3,1,""],get_name:[1,3,1,""],get_patch:[1,3,1,""],holepoints:[1,1,1,""],holes:[1,1,1,""],label:[1,3,1,""],line_from_startpt:[1,3,1,""],line_insert:[1,3,1,""],lines:[1,1,1,""],matl:[1,1,1,""],nodes:[1,1,1,""],part:[1,1,1,""],plot:[1,3,1,""],points:[1,1,1,""],rem_sline:[1,3,1,""],set_child_ccxtypes:[1,3,1,""],set_esize:[1,3,1,""],set_etype:[1,3,1,""],signlines:[1,1,1,""],update:[1,3,1,""]},"pycalculix.geometry.Line":{add_signline:[1,3,1,""],allpoints:[1,3,1,""],arc_tang_intersection:[1,3,1,""],areas:[1,3,1,""],coincident:[1,3,1,""],edge:[1,1,1,""],ediv:[1,1,1,""],get_abc:[1,3,1,""],get_name:[1,3,1,""],get_perp_vec:[1,3,1,""],get_tan_vec:[1,3,1,""],intersects:[1,3,1,""],length:[1,3,1,""],mid:[1,3,1,""],midpt:[1,1,1,""],nodes:[1,1,1,""],offset:[1,3,1,""],points:[1,1,1,""],pt:[1,3,1,""],reverse:[1,3,1,""],save_to_points:[1,3,1,""],set_ediv:[1,3,1,""],set_esize:[1,3,1,""],set_pt:[1,3,1,""],signed_copy:[1,3,1,""],signlines:[1,1,1,""],touches:[1,3,1,""]},"pycalculix.geometry.LineLoop":{append:[1,3,1,""],area:[1,1,1,""],ccw:[1,1,1,""],center:[1,1,1,""],closed:[1,1,1,""],contains_point:[1,3,1,""],get_patch:[1,3,1,""],hole:[1,1,1,""],insert:[1,3,1,""],inside:[1,3,1,""],parent:[1,1,1,""],remove:[1,3,1,""],reverse:[1,3,1,""],set_id:[1,3,1,""],set_parent:[1,3,1,""]},"pycalculix.geometry.Point":{ang_bet_deg:[1,3,1,""],ang_bet_rad:[1,3,1,""],ang_deg:[1,3,1,""],ang_rad:[1,3,1,""],arc_center:[1,1,1,""],axrad:[1,3,1,""],get_name:[1,3,1,""],label:[1,3,1,""],length:[1,3,1,""],lines:[1,1,1,""],make_unit:[1,3,1,""],nodes:[1,1,1,""],plot:[1,3,1,""],radax:[1,3,1,""],rot_ccw_deg:[1,3,1,""],set_esize:[1,3,1,""],set_line:[1,3,1,""],unset_line:[1,3,1,""],x:[1,1,1,""],y:[1,1,1,""],z:[1,1,1,""]},"pycalculix.geometry.SignArc":{actr:[1,3,1,""],concavity:[1,3,1,""],edge:[1,3,1,""],faces:[1,1,1,""],get_name:[1,3,1,""],label:[1,3,1,""],line:[1,1,1,""],lineloop:[1,1,1,""],midpt:[1,3,1,""],n1:[1,1,1,""],nodes:[1,1,1,""],plot:[1,3,1,""],points:[1,3,1,""],pt:[1,3,1,""],radius:[1,3,1,""],reverse:[1,3,1,""],set_ediv:[1,3,1,""],set_esize:[1,3,1,""],set_lineloop:[1,3,1,""],set_pt:[1,3,1,""],sign:[1,1,1,""],signed_copy:[1,3,1,""]},"pycalculix.geometry.SignLine":{edge:[1,3,1,""],faces:[1,1,1,""],get_name:[1,3,1,""],label:[1,3,1,""],line:[1,1,1,""],lineloop:[1,1,1,""],midpt:[1,3,1,""],n1:[1,1,1,""],nodes:[1,1,1,""],plot:[1,3,1,""],points:[1,3,1,""],pt:[1,3,1,""],reverse:[1,3,1,""],set_ediv:[1,3,1,""],set_esize:[1,3,1,""],set_lineloop:[1,3,1,""],set_pt:[1,3,1,""],sign:[1,1,1,""],signed_copy:[1,3,1,""]},"pycalculix.installer":{add:[1,4,1,""],add_remove_dll_links:[1,4,1,""],find_brew_binary_location:[1,4,1,""],get_direct_url:[1,4,1,""],href_from_link_text:[1,4,1,""],mac_add:[1,4,1,""],mac_add_ccx:[1,4,1,""],mac_remove:[1,4,1,""],printos:[1,4,1,""],remove:[1,4,1,""],remove_like:[1,4,1,""],ubuntu_add:[1,4,1,""],ubuntu_remove:[1,4,1,""],win_add_ccx:[1,4,1,""],win_add_gmsh:[1,4,1,""],windows_add:[1,4,1,""],windows_remove:[1,4,1,""],zipfile_by_bitsize:[1,4,1,""]},"pycalculix.loads":{ConstLoad:[1,2,1,""],LinearLoad:[1,2,1,""]},"pycalculix.loads.ConstLoad":{ccx:[1,3,1,""],get_list:[1,3,1,""]},"pycalculix.loads.LinearLoad":{ccx:[1,3,1,""],get_list:[1,3,1,""],get_val:[1,3,1,""]},"pycalculix.material":{Material:[1,2,1,""]},"pycalculix.material.Material":{ccx:[1,3,1,""],set_mech_props:[1,3,1,""],set_therm_expan:[1,3,1,""],set_therm_props:[1,3,1,""]},"pycalculix.mesh":{Element:[1,2,1,""],Face:[1,2,1,""],Node:[1,2,1,""]},"pycalculix.mesh.Element":{calc_center:[1,3,1,""],ccx:[1,3,1,""],ccxtype:[1,1,1,""],center:[1,1,1,""],face:[1,1,1,""],faces:[1,1,1,""],get_area:[1,3,1,""],get_name:[1,3,1,""],get_poly:[1,3,1,""],get_tris:[1,3,1,""],id:[1,1,1,""],label:[1,3,1,""],node:[1,1,1,""],nodes:[1,1,1,""],set_ccxtype:[1,3,1,""]},"pycalculix.mesh.Face":{element:[1,1,1,""],ext:[1,1,1,""],get_mnorm:[1,3,1,""],id:[1,1,1,""],length:[1,3,1,""],nmid:[1,1,1,""],nodes:[1,1,1,""],set_ext:[1,3,1,""],set_nmid:[1,3,1,""]},"pycalculix.mesh.Node":{add_element:[1,3,1,""],add_face:[1,3,1,""],ccx:[1,3,1,""],elements:[1,1,1,""],faces:[1,1,1,""],get_name:[1,3,1,""],id:[1,1,1,""],label:[1,3,1,""],order:[1,1,1,""],set_order:[1,3,1,""],x:[1,1,1,""],y:[1,1,1,""],z:[1,1,1,""]},"pycalculix.partmodule":{Part:[1,2,1,""]},"pycalculix.partmodule.Part":{"goto":[1,3,1,""],__cursor:[1,1,1,""],__fea:[1,1,1,""],__holemode:[1,1,1,""],allpoints:[1,1,1,""],areas:[1,1,1,""],bottom:[1,1,1,""],center:[1,1,1,""],chunk:[1,3,1,""],draw_arc:[1,3,1,""],draw_arc_angle:[1,3,1,""],draw_circle:[1,3,1,""],draw_hole:[1,3,1,""],draw_line_ax:[1,3,1,""],draw_line_delta:[1,3,1,""],draw_line_rad:[1,3,1,""],draw_line_to:[1,3,1,""],elements:[1,1,1,""],fillet_all:[1,3,1,""],fillet_lines:[1,3,1,""],get_item:[1,3,1,""],get_name:[1,3,1,""],label:[1,3,1,""],left:[1,1,1,""],lines:[1,1,1,""],nodes:[1,1,1,""],plot:[1,3,1,""],points:[1,1,1,""],right:[1,1,1,""],signlines:[1,1,1,""],top:[1,1,1,""]},"pycalculix.problem":{Problem:[1,2,1,""]},"pycalculix.problem.Problem":{__ptype:[1,1,1,""],fea:[1,1,1,""],fname:[1,1,1,""],rfile:[1,1,1,""],solve:[1,3,1,""],solved:[1,1,1,""]},"pycalculix.results_file":{ResultsFile:[1,2,1,""]},"pycalculix.results_file.ResultsFile":{__problem:[1,1,1,""],__results:[1,1,1,""],__steps:[1,1,1,""],__time:[1,1,1,""],check_ccx_version:[1,3,1,""],eplot:[1,3,1,""],get_emax:[1,3,1,""],get_emin:[1,3,1,""],get_eval:[1,3,1,""],get_fsum:[1,3,1,""],get_nmax:[1,3,1,""],get_nmin:[1,3,1,""],get_nval:[1,3,1,""],get_relative_gradient:[1,3,1,""],load:[1,3,1,""],nplot:[1,3,1,""],plot_gradient:[1,3,1,""],set_time:[1,3,1,""],steps:[1,3,1,""],time:[1,3,1,""]},"pycalculix.selector":{Selector:[1,2,1,""]},"pycalculix.selector.Selector":{__all:[1,1,1,""],__areas:[1,1,1,""],__elements:[1,1,1,""],__faces:[1,1,1,""],__fea:[1,1,1,""],__nodes:[1,1,1,""],__parts:[1,1,1,""],__points:[1,1,1,""],__slines:[1,1,1,""],allsel_under:[1,3,1,""],allselect:[1,3,1,""],areas:[1,1,1,""],deselect:[1,3,1,""],deselect_all:[1,3,1,""],elements:[1,1,1,""],faces:[1,1,1,""],lines:[1,1,1,""],nodes:[1,1,1,""],parts:[1,1,1,""],points:[1,1,1,""],print_summary:[1,3,1,""],select:[1,3,1,""],select_above:[1,3,1,""],select_above_all:[1,3,1,""],select_all:[1,3,1,""],select_below:[1,3,1,""],select_below_all:[1,3,1,""],select_none:[1,3,1,""]},pycalculix:{base_classes:[1,0,0,"-"],cadimporter:[1,0,0,"-"],components:[1,0,0,"-"],connectors:[1,0,0,"-"],environment:[1,0,0,"-"],feamodel:[1,0,0,"-"],geometry:[1,0,0,"-"],installer:[1,0,0,"-"],loads:[1,0,0,"-"],material:[1,0,0,"-"],mesh:[1,0,0,"-"],partmodule:[1,0,0,"-"],problem:[1,0,0,"-"],results_file:[1,0,0,"-"],selector:[1,0,0,"-"],version:[1,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","attribute","Python attribute"],"2":["py","class","Python class"],"3":["py","method","Python method"],"4":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:attribute","2":"py:class","3":"py:method","4":"py:function"},terms:{"boolean":1,"class":1,"const":1,"default":1,"enum":1,"float":1,"function":1,"goto":1,"import":1,"int":1,"long":1,"new":1,"return":1,"static":1,"switch":1,"true":1,For:1,MKS:1,Pos:1,That:1,The:1,Thes:1,__all:1,__area:1,__cursor:1,__element:1,__face:1,__fea:1,__fname:1,__holemod:1,__layer:1,__node:1,__part:1,__point:1,__problem:1,__ptype:1,__result:1,__sline:1,__step:1,__swapxi:1,__time:1,_ctype:1,abc:1,about:1,abov:1,acceler:1,accept:1,act:1,actr:1,adapt:1,add:1,add_el:1,add_fac:1,add_hole_slin:1,add_remove_dll_link:1,add_signlin:1,add_slin:1,added:1,afil:1,after:1,agr:1,align:1,all:1,allow:1,allpoint:1,allsel_und:1,allselect:1,alpha:1,also:1,analysi:1,analyz:1,ang:1,ang_bet_deg:1,ang_bet_rad:1,ang_deg:1,ang_rad:1,angl:1,ani:1,answer:1,anum:1,apach:1,api:1,append:1,appli:1,applic:1,apppli:1,apt:1,arc:1,arc_cent:1,arc_end_point:1,arc_start_point:1,arc_tang_intersect:1,area:1,arg:1,argument:1,around:1,arrow:1,ask:1,assign:1,assum:1,attribut:1,automat:1,averag:1,avg:1,awai:1,axi:1,axial:1,axisym:1,axisymmetr:1,axrad:1,back:1,ballpark:1,base:1,base_class:0,becom:1,been:1,befor:1,begin:1,behavior:1,below:1,between:1,binari:1,binaries_url:1,bit:1,bitsiz:1,bool:1,both:1,bottom:1,bound:1,boundari:1,branch:1,brew:1,build:1,byfac:1,caclul:1,cad:1,cadimport:0,calc:1,calc_area_cent:1,calc_cent:1,calcul:1,calculix:1,calcult:1,call:1,can:1,capabl:1,cax3:1,cax4:1,cax6:1,cax8:1,ccw:1,ccx:1,ccxtype:1,celsiu:1,center:1,center_i:1,center_x:1,centroid:1,cfswitch:1,cgx:1,chang:1,charact:1,check:1,check_ccx_vers:1,child:1,children:1,choos:1,chunk:1,chunk_list:1,circl:1,clockwis:1,close:1,closur:1,cname:1,coarser:1,code:1,coincid:1,collect:1,color:1,colorbar:1,com:1,comp:1,compliant:1,compon:0,compres:1,compress:1,comput:1,concav:1,condit:1,conduct:1,connect:1,connector:0,consol:1,constant:1,constload:1,constr:1,constraint:1,contact:1,contain:1,contains_point:1,content:0,convers:1,convert:1,convex:1,coordin:1,copi:1,corder:1,corner:1,correct:1,correctli:1,cose:1,counter:1,counterclockwis:1,cpe3:1,cpe4:1,cpe6:1,cpe8:1,cps3:1,cps4:1,cps6:1,cps8:1,creat:1,cross:1,ctr:1,ctype:1,curent:1,current:1,cursor:1,curv:1,curve_fit:1,custom:1,cut:1,data:1,databas:1,debug:1,defin:1,definit:1,degre:1,degrees_ccw:1,delet:1,delta:1,delta_i:1,delta_x:1,densiti:1,depend:1,dependednt:1,describ:1,descript:1,deselect:1,deselect_al:1,diagram:1,dict:1,dictionari:1,differ:1,direct:1,dirrent:1,displ:1,displac:1,displai:1,displayed_node_loc:1,dist:1,dist_unit:1,distanc:1,divid:1,divis:1,dll:1,document:1,doe:1,doesn:1,don:1,doubl:1,download:1,dpi:1,draw:1,draw_arc:1,draw_arc_angl:1,draw_circl:1,draw_hol:1,draw_line_ax:1,draw_line_delta:1,draw_line_rad:1,draw_line_to:1,dwbuildnumb:1,dwmajorvers:1,dwminorvers:1,dwosversioninfos:1,dwplatformid:1,dx_rad:1,dy_ax:1,each:1,edg:1,ediv:1,eeqv:1,elast:1,eleemnt:1,element:1,element_ord:1,element_shap:1,element_typ:1,emphas:1,empti:1,end:1,end_i:1,end_point:1,end_pt:1,end_x:1,energi:1,entiti:1,envion:1,environ:0,eorder:1,eplot:1,equat:1,error:1,eshap:1,esiz:1,etc:1,etyp:1,everi:1,everyth:1,examp:1,exampl:1,except:1,exclud:1,exclude_convex:1,exi:1,exist:1,exlin:1,expans:1,explicitli:1,expon:1,exponenti:1,ext:1,exterior:1,extern:1,eyz:1,ezx:1,face:1,factor:1,fahrenheit:1,fail:1,fals:1,fbd:1,fea:1,feamodel:0,field:1,fieldtyp:1,file:1,fill:1,fillet:1,fillet_al:1,fillet_lin:1,find:1,find_brew_binary_loc:1,fine:1,finit:1,first:1,fit:1,fix:1,flag:1,flatten:1,flip:1,fluid:1,fname:1,fnum:1,folder:1,folder_dl:1,folder_dll_link:1,folder_from:1,folder_to:1,follow:1,foot:1,forc:1,formula:1,found:1,from:1,fromunit:1,full:1,further:1,geo:1,geometr:1,geometri:0,get:1,get_abc:1,get_ang:1,get_ang_rad:1,get_area:1,get_children:1,get_concav:1,get_direct_url:1,get_dpi:1,get_emax:1,get_emin:1,get_ev:1,get_fsum:1,get_id:1,get_item:1,get_list:1,get_maxid:1,get_maxlength:1,get_minid:1,get_mnorm:1,get_nam:1,get_next_id:1,get_nmax:1,get_nmin:1,get_nval:1,get_patch:1,get_path:1,get_perp_vec:1,get_poli:1,get_pt_at:1,get_relative_gradi:1,get_tan_vec:1,get_text_hv:1,get_tri:1,get_unit:1,get_val:1,get_vers:1,get_verts_cod:1,github:1,give:1,given:1,gmh:1,gmsh:1,gmult:1,goe:1,gradient:1,grav:1,graviti:1,hand:1,hard:1,has:1,have:1,header:1,heat:1,height:1,helper:1,here:1,high:1,higher:1,highest:1,hole:1,hole_bool:1,hole_lin:1,holemod:1,holepoint:1,homebrew:1,horizont:1,how:1,href_from_link_text:1,html:1,http:1,ident:1,identifi:1,idget:1,idnum:1,idobj:1,ids:1,imag:1,inch:1,includ:1,inclus:1,ind:1,index:[0,1],inlist:1,inp:1,input:1,insert:1,insid:1,instal:0,instanc:1,int_typ:1,integr:1,interact:1,interect:1,interfac:1,intern:1,intersect:1,invers:1,ipnum:1,ipoint:1,issu:1,item:1,item_list:1,itemlist:1,its:1,just:1,justinablack:1,karg:1,keep:1,kei:1,kept:1,keypoint:1,kilogram:1,kval:1,label:1,last:1,layer:1,lbf:1,ldir:1,left:1,leftmost:1,legend:1,len:1,length:1,level:1,lgiven:1,librari:1,like:1,line1:1,line2:1,line:1,line_from_startpt:1,line_insert:1,line_list:1,linear:1,linearload:1,lineloop:1,link:1,link_text:1,linux:1,list:1,listifi:1,lnew:1,lnum:1,load:0,locat:1,look:1,loop:1,low:1,ltype:1,lval:1,lvect:1,mac:1,mac_add:1,mac_add_ccx:1,mac_remov:1,made:1,mag:1,magnitud:1,mai:1,make:1,make_matl:1,make_part:1,make_problem:1,make_unit:1,mani:1,many_si:1,mass:1,master:1,master_comp:1,master_lin:1,match:1,materi:0,mathworld:1,matl:1,matlotlib:1,matplotlib:1,max:1,max_val:1,maximum:1,mean:1,mechan:1,mechtpy:1,mechtyp:1,member:1,mesh:0,mesher:1,meshlist:1,meshmod:1,meter:1,meteri:1,method:1,metric:1,mid:1,midpoint:1,midpt:1,midsid:1,milimet:1,min:1,min_val:1,mine:1,minut:1,mode:1,model:1,model_nam:1,model_node_loc:1,modifi:1,modul:0,modulu:1,monitor:1,more:1,move:1,mpa:1,mult:1,multipl:1,multipli:1,must:1,n_poli:1,n_subpoint:1,name:1,need:1,neg:1,newpt:1,next:1,nlist:1,nmid:1,nnum:1,nodal:1,nodal_thick:1,node1:1,node2:1,node:1,node_displac:1,node_num:1,non:1,nondim:1,none:1,nonlinear:1,norm_vect:1,normal:1,normal_vector:1,note:1,nplot:1,nshow:1,num_arc:1,number:1,object:1,offset:1,old:1,one:1,ones:1,onli:1,onto:1,oper:1,option:1,order:1,org:1,orient:1,origina:1,os_major:1,os_minor:1,osgood:1,osversioninfoexw:1,other:1,otherwis:1,out:1,over:1,overwit:1,packag:0,package_fold:1,page:[0,1],pair:1,paramet:1,parent:1,parent_lin:1,part0:1,part:1,partmodul:0,pass:1,patch:1,path:1,pend:1,per:1,perpendicular:1,perpendiculat:1,pick:1,plane:1,plate:1,plot:1,plot_area:1,plot_constraint:1,plot_el:1,plot_finish:1,plot_geometri:1,plot_gradi:1,plot_lin:1,plot_multipl:1,plot_nod:1,plot_part:1,plot_point:1,plot_pressur:1,plot_set_bound:1,plstrain:1,plstress:1,plt:1,png:1,pnum:1,point:1,point_end:1,point_first:1,point_last:1,point_start:1,poisson:1,poisson_ratio:1,polygon:1,polygonarea:1,portion:1,pos:1,posit:1,possibl:1,postiv:1,postprocessor:1,power:1,pratio:1,prefix:1,preprocessor:1,prerocessor:1,press:1,press_fluid:1,presssur:1,pressur:1,presur:1,princip:1,print:1,print_summari:1,printo:1,problem:0,problem_typ:1,progeni:1,program:1,program_nam:1,project:1,properti:1,provid:1,psf:1,psi:1,pstart:1,put:1,python:1,quad1axisym:1,quad1plstrain:1,quad1plstress:1,quad2axisym:1,quad2plstrain:1,quad2plstress:1,quad:1,quadrangl:1,quadrilater:1,quadrilateri:1,queri:1,rad:1,radax:1,radial:1,radian:1,radiu:1,radp:1,rais:1,ramberg:1,ratio:1,reaction:1,read:1,reccomend:1,reduc:1,regist:1,rel:1,rem_slin:1,remov:1,remove_lik:1,repo:1,report:1,request:1,requir:1,res:1,resfield:1,resfil:1,result:1,results_fil:0,resultsfil:1,resv:1,revers:1,rfile:1,rho:1,right:1,rightmost:1,rot_ccw_deg:1,rotat:1,rpm:1,rule:1,sai:1,same:1,save:1,save_to_point:1,scalar:1,scale:1,sea:1,search:[0,1],search_path:1,search_str:1,second:1,see:1,sel_typ:1,select:1,select_abov:1,select_above_al:1,select_al:1,select_below:1,select_below_al:1,select_non:1,selector:0,self:1,send:1,seqv:1,server:1,set:1,set_ccxtyp:1,set_child_ccxtyp:1,set_constr:1,set_contact_linear:1,set_ediv:1,set_es:1,set_eshap:1,set_etyp:1,set_ext:1,set_fluid_press:1,set_grav:1,set_id:1,set_lin:1,set_lineloop:1,set_load:1,set_matl:1,set_mech_prop:1,set_minid:1,set_nam:1,set_nmid:1,set_ord:1,set_par:1,set_pt:1,set_radp:1,set_rpm:1,set_therm_expan:1,set_therm_prop:1,set_tim:1,set_unit:1,sgn:1,shape:1,share:1,should:1,show:1,shown:1,side:1,sign:1,signarc:1,signed_copi:1,signlin:1,simplic:1,singarc:1,singl:1,size:1,slave:1,slave_comp:1,slave_lin:1,slinch:1,sline:1,slug:1,small:1,smaller:1,solid:1,solv:1,solver:1,sourc:1,sovler:1,spaceth:1,spec_heat:1,specif:1,specifc_heat:1,split:1,start:1,start_point:1,start_pt:1,start_valu:1,step:1,stiff:1,storag:1,store:1,str:1,strain:1,strese:1,stress:1,string:1,struct:1,structur:1,styledict:1,sub:1,subdivid:1,submodul:0,succeed:1,sum:1,summari:1,support:1,surf_int:1,surf_to_surf:1,surfac:1,surfaceinteract:1,surfint:1,swap:1,swapxi:1,swept:1,sxy:1,system:1,syz:1,szcsdversion:1,szx:1,tabl:1,tangenc:1,tangent:1,tell:1,temp:1,temperatur:1,tension:1,term:1,text:1,thei:1,them:1,thermal:1,thermal_exp:1,thermal_expans:1,thi:1,thick:1,third:1,those:1,through:1,time:1,timeout:1,titl:1,tmpline:1,tonn:1,too:1,tool:1,top:1,touch:1,tounit:1,toward:1,travers:1,tri1axisym:1,tri1plstrain:1,tri1plstress:1,tri2axisym:1,tri2plstrain:1,tri2plstress:1,tri:1,tri_api:1,triangl:1,triangul:1,tupl:1,turn:1,two:1,type:1,tzero:1,ubuntu:1,ubuntu_add:1,ubuntu_remov:1,undeform:1,under:1,union:1,uniqu:1,unit:1,unitless:1,unitstr:1,unset_lin:1,unus:1,updat:1,url:1,use:1,used:1,user:1,uses:1,using:1,utot:1,val:1,valid:1,valu:1,vari:1,vector:1,veri:1,version:0,version_str:1,vert:1,vertic:1,view:1,volum:1,want:1,water:1,when:1,where:1,whether:1,which:1,win_add_ccx:1,win_add_gmsh:1,window:1,windows_add:1,windows_remov:1,within:1,without:1,wolfram:1,wproducttyp:1,wreserv:1,write:1,write_cgx:1,write_gmsh:1,wservicepackmajor:1,wservicepackminor:1,wsuitemask:1,xval:1,yellow:1,yield:1,yield_offset:1,yield_stress:1,you:1,young:1,zero:1,zip:1,zipfil:1,zipfile_by_bits:1,zipfile_regex:1},titles:["Welcome to pycalculix\u2019s documentation!","pycalculix package"],titleterms:{base_class:1,cadimport:1,compon:1,connector:1,content:1,document:0,environ:1,feamodel:1,geometri:1,indic:0,instal:1,load:1,materi:1,mesh:1,modul:1,packag:1,partmodul:1,problem:1,pycalculix:[0,1],results_fil:1,selector:1,submodul:1,tabl:0,version:1,welcom:0}}) \ No newline at end of file diff --git a/dist/examples.zip b/dist/examples.zip index b5649bf..727b528 100644 Binary files a/dist/examples.zip and b/dist/examples.zip differ diff --git a/dist/pycalculix-1.1.2.zip b/dist/pycalculix-1.1.4.zip similarity index 73% rename from dist/pycalculix-1.1.2.zip rename to dist/pycalculix-1.1.4.zip index 9150a80..403aaa9 100644 Binary files a/dist/pycalculix-1.1.2.zip and b/dist/pycalculix-1.1.4.zip differ diff --git a/docs/Makefile b/docs/Makefile index 298ea9e..d4bb2cb 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,9 +1,10 @@ # Minimal makefile for Sphinx documentation # -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build @@ -16,4 +17,4 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py index edba93a..612d6f3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,10 +1,8 @@ -# -*- coding: utf-8 -*- -# # Configuration file for the Sphinx documentation builder. # -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- @@ -20,21 +18,18 @@ # -- Project information ----------------------------------------------------- project = 'pycalculix' -copyright = '2018, Justin Black' +copyright = '2019, Justin Black' author = 'Justin Black' # The short X.Y version -version = '1.1.2' +version = '1.1.4' + # The full version, including alpha/beta/rc tags -release = '1.1.2' +release = '1.1.4' # -- General configuration --------------------------------------------------- -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. @@ -47,15 +42,6 @@ # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # @@ -68,9 +54,6 @@ # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - # -- Options for HTML output ------------------------------------------------- @@ -79,102 +62,11 @@ # html_theme = 'alabaster' -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'pycalculixdoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'pycalculix.tex', 'pycalculix Documentation', - 'Justin Black', 'manual'), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'pycalculix', 'pycalculix Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'pycalculix', 'pycalculix Documentation', - author, 'pycalculix', 'One line description of project.', - 'Miscellaneous'), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - # -- Extension configuration ------------------------------------------------- diff --git a/docs/index.rst b/docs/index.rst index 884a439..a21a963 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ .. pycalculix documentation master file, created by - sphinx-quickstart on Sun Dec 23 14:21:14 2018. + sphinx-quickstart on Sat Nov 30 13:33:30 2019. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. diff --git a/docs/make.bat b/docs/make.bat index 27f573b..2119f51 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -25,11 +25,11 @@ if errorlevel 9009 ( exit /b 1 ) -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd diff --git a/docs/pycalculix.rst b/docs/pycalculix.rst index fda3e29..74ad3c5 100644 --- a/docs/pycalculix.rst +++ b/docs/pycalculix.rst @@ -8,135 +8,135 @@ pycalculix.base\_classes module ------------------------------- .. automodule:: pycalculix.base_classes - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.cadimporter module ----------------------------- .. automodule:: pycalculix.cadimporter - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.components module ---------------------------- .. automodule:: pycalculix.components - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.connectors module ---------------------------- .. automodule:: pycalculix.connectors - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.environment module ----------------------------- .. automodule:: pycalculix.environment - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.feamodel module -------------------------- .. automodule:: pycalculix.feamodel - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.geometry module -------------------------- .. automodule:: pycalculix.geometry - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.installer module --------------------------- .. automodule:: pycalculix.installer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.loads module ----------------------- .. automodule:: pycalculix.loads - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.material module -------------------------- .. automodule:: pycalculix.material - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.mesh module ---------------------- .. automodule:: pycalculix.mesh - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.partmodule module ---------------------------- .. automodule:: pycalculix.partmodule - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.problem module ------------------------- .. automodule:: pycalculix.problem - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.results\_file module ------------------------------- .. automodule:: pycalculix.results_file - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.selector module -------------------------- .. automodule:: pycalculix.selector - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: pycalculix.version module ------------------------- .. automodule:: pycalculix.version - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: Module contents --------------- .. automodule:: pycalculix - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/pycalculix/version.py b/pycalculix/version.py index 0b2f79d..c72e379 100644 --- a/pycalculix/version.py +++ b/pycalculix/version.py @@ -1 +1 @@ -__version__ = "1.1.3" +__version__ = "1.1.4" diff --git a/setup.py b/setup.py index 635fe71..adabb0b 100644 --- a/setup.py +++ b/setup.py @@ -31,49 +31,57 @@ "Topic :: Scientific/Engineering :: Physics", "Topic :: Scientific/Engineering"], long_description = """\ -''' -Python 3 library to automate and build finite element analysis (FEA) models in Calculix. ----------------------------------------------------------------------------------------- -Meshing uses Calculix or GMSH. -Website: http://justinablack.com/pycalculix/ -Source Code: https://github.com/spacether/pycalculix -Documentation: https://pythonhosted.org/pycalculix/ +Python 3 library to automate and build finite element analysis (FEA) models in Calculix +================================================================================================ + +* Meshing uses Calculix or GMSH +* Website: http://justinablack.com/pycalculix/ +* Source Code: https://github.com/spacether/pycalculix +* Documentation: https://pythonhosted.org/pycalculix/ Useful applications of Pycalculix: --Trade studies for plane stress, plane strain, or axisymmetric parts --Quick Kt analysis of 2D geometry --Learning finite element analyis (FEA) and Python +----------------------------------- + +* Trade studies for plane stress, plane strain, or axisymmetric parts +* Quick Kt analysis of 2D geometry +* Learning finite element analyis (FEA) and Python Notes: +~~~~~~~~~~~~~~~~~~~~~~ I build a chunker in python which tries to cut big areas (> 5 sides) which cgx can't mesh into smaller areas (<= 5 sides) which are meshable in cgx. The chunker may not always be able to cut areas correctly. Elements Supported: +~~~~~~~~~~~~~~~~~~~~~~ Axisymmetric, plane stress, and plane strain elements are supported. First and second order triangles and quadrilaterals are supported. - First order elements only have corner nodes - Second order elements have corner and mid-side nodes + +* First order elements only have corner nodes +* Second order elements have corner and mid-side nodes + Second order elements produce more accurate results Setting element divisions on lines is supported Geometry Building: +~~~~~~~~~~~~~~~~~~~~~~ One can build separate parts made of points, lines, arcs, and areas. One can draw a part made of straight lines, then smooth out corners by adding blends/fillets with the part method: part.fillet_lines(L1, L2, arc_radius) The new fillet will be tangent to both adjacent lines. Loading: -Force loading -Constant pressure -Linearly varying pressure (water pressure) -Gravity -Rotational speed forces -Displacement constraints are supported -Loads are stored on geometry primitives (points lines etc) and can be applied -before or after meshing. +~~~~~~~~~~~~~~~~~~~~~~ +* Force loading +* Constant pressure +* Linearly varying pressure (water pressure) +* Gravity +* Rotational speed forces +* Displacement constraints are supported +* Loads are stored on geometry primitives (points lines etc) and can be applied before or after meshing Examples: +~~~~~~~~~~~~~~~~~~~~~~ https://github.com/spacether/pycalculix/tree/master/examples """ )