Source code for fairyfly_therm.simulation.mesh

# coding=utf-8
"""Meshing control parameters."""
from __future__ import division
import xml.etree.ElementTree as ET

from fairyfly.typing import float_in_range, int_positive


[docs] class MeshControl(object): """Meshing control parameters. Args: mesh_type: Text to indicate the type of meshing algorithm to use. Choose from the following. Simmetrix is generally more flexible and capable of handling more complex geometry when compared with the QuatTree. However, the structure of QuadTree meshes is more predictable. (Default: Simmetrix). * Simmetrix * QuadTree parameter: A positive integer for the minimum number of subdivisions to be performed while meshing the input geometry. The higher the mesh control parameter, the smaller the maximum size of finite elements in the model and the smoother the results will appear. However, higher mesh parameters will also require more time to run. (Default: 20). run_error_estimator: Boolean to note whether the error estimator should be run as part of the finite element analysis. If the global error is above a specified value, then the error estimator signals the mes generator, and the mesh is refined in areas where the potential for error is high. The refined mesh is sent back to the finite element solver, and a new solution is obtained. (Default: True). max_error_percent: A number between 0 and 100 for the percent error energy norm used by the error estimator. This is the maximum value of the error energy divided by the energy of the sum of the recovered fluxes and the error, multiplied by 100. (Default: 10). max_iterations: A positive integer for the number of iterations between the error estimator and the solver to be performed before the finding a solution is abandoned and the program exits. (Default: 5). Properties: * mesh_type * parameter * run_error_estimator * max_error_percent * max_iterations """ __slots__ = ('_mesh_type', '_parameter', '_run_error_estimator', '_max_error_percent', '_max_iterations') TYPES = ('Simmetrix', 'QuadTree') def __init__(self, mesh_type='Simmetrix', parameter=20, run_error_estimator=True, max_error_percent=10, max_iterations=5): """Initialize MeshControl.""" self._parameter = 8 # dummy value to ensure checks pass self.mesh_type = mesh_type self.parameter = parameter self.run_error_estimator = run_error_estimator self.max_error_percent = max_error_percent self.max_iterations = max_iterations @property def mesh_type(self): """Get or set text for the meshing algorithm to be used.""" return self._mesh_type @mesh_type.setter def mesh_type(self, value): if value is not None: clean_input = str(value).lower() for key in self.TYPES: if key.lower() == clean_input: value = key break else: raise ValueError( 'Mesh control mesh_type "{}" is not supported.\n' 'Choose from the following:\n{}'.format( value, '\n'.join(self.TYPES))) self._mesh_type = value else: self._mesh_type = self.TYPES[0] self._check_type_and_parameter() @property def parameter(self): """Get or set a positive integer for the minimum number of mesh subdivisions.""" return self._parameter @parameter.setter def parameter(self, value): self._parameter = int_positive(value, 'MeshControl parameter') assert self._parameter > 1, 'MeshControl parameter must be greater than 1.' self._check_type_and_parameter() @property def run_error_estimator(self): """Get or set a boolean for whether to run the error estimator.""" return self._run_error_estimator @run_error_estimator.setter def run_error_estimator(self, value): self._run_error_estimator = bool(value) @property def max_error_percent(self): """Get or set a number for the maximum accepted percentage of error.""" return self._max_error_percent @max_error_percent.setter def max_error_percent(self, value): self._max_error_percent = \ float_in_range(value, 0, 100, 'MeshControl error percent') @property def max_iterations(self): """Get or set a positive integer for the iterations between the mesher and solver. """ return self._max_iterations @max_iterations.setter def max_iterations(self, value): self._max_iterations = int_positive(value, 'MeshControl max iterations') assert self._max_iterations > 1, \ 'MeshControl max iterations must be greater than 1.' def _check_type_and_parameter(self): if self.mesh_type == 'QuadTree' and self.parameter > 8: msg = 'THERM QuadTree mesh type only supports a meshing parameter ' \ 'up to 8.\nCurrent meshing parameter is {}.'.format(self.parameter) raise ValueError(msg)
[docs] @classmethod def from_therm_xml(cls, xml_element): """Create MeshControl from an XML element of a THERM MeshControl. Args: xml_element: An XML element of a THERM MeshControl. """ xml_type = xml_element.find('MeshType') mesh_type = 'Simmetrix' \ if xml_type.text == 'Simmetrix Version 2022' else 'QuadTree' xml_param = xml_element.find('MeshParameter') xml_run = xml_element.find('RunErrorEstimator') run_err = True if xml_run.text == 'true' else False xml_error = xml_element.find('ErrorEnergyNorm') xml_iter = xml_element.find('MaximumIterations') return MeshControl(mesh_type, xml_param.text, run_err, xml_error.text, xml_iter.text)
[docs] @classmethod def from_therm_xml_str(cls, xml_str): """Create a MeshControl from an XML text string of a THERM MeshControl. Args: xml_str: An XML text string of a THERM MeshControl. """ root = ET.fromstring(xml_str) return cls.from_therm_xml(root)
[docs] @classmethod def from_dict(cls, data): """Create a MeshControl from a dictionary. Args: data: A python dictionary in the following format .. code-block:: python { "type": 'MeshControl', "mesh_type": 'Simmetrix', "parameter": 50, "run_error_estimator": True, "max_error_percent": 10, "max_iterations": 5 } """ assert data['type'] == 'MeshControl', \ 'Expected MeshControl. Got {}.'.format(data['type']) type = data['mesh_type'] if 'mesh_type' in data and \ data['mesh_type'] is not None else 'Simmetrix' param = data['parameter'] if 'parameter' in data else 20 run = data['run_error_estimator'] if 'run_error_estimator' in data else True error = data['max_error_percent'] if 'max_error_percent' in data else 10 iter = data['max_iterations'] if 'max_iterations' in data else 5 return cls(type, param, run, error, iter)
[docs] def to_therm_xml(self, calculation_element=None): """Get an THERM XML element of the MeshControl. Args: calculation_element: An optional XML Element for the CalculationOptions to which the generated objects will be added. If None, a new XML Element will be generated. .. code-block:: xml <MeshControl> <MeshType>QuadTree Mesher</MeshType> <MeshParameter>3</MeshParameter> <RunErrorEstimator>true</RunErrorEstimator> <ErrorEnergyNorm>10</ErrorEnergyNorm> <MaximumIterations>5</MaximumIterations> </MeshControl> """ # create a new Materials element if one is not specified if calculation_element is not None: xml_mesh = ET.SubElement(calculation_element, 'MeshControl') else: xml_mesh = ET.Element('MeshControl') # add all of the required basic attributes xml_type = ET.SubElement(xml_mesh, 'MeshType') xml_type.text = 'Simmetrix Version 2022' \ if self.mesh_type == 'Simmetrix' else 'QuadTree Mesher' xml_param = ET.SubElement(xml_mesh, 'MeshParameter') xml_param.text = str(self.parameter) xml_run = ET.SubElement(xml_mesh, 'RunErrorEstimator') xml_run.text = 'true' if self.run_error_estimator else 'false' xml_error = ET.SubElement(xml_mesh, 'ErrorEnergyNorm') xml_error.text = str(self.max_error_percent) xml_iter = ET.SubElement(xml_mesh, 'MaximumIterations') xml_iter.text = str(self.max_iterations) return xml_mesh
[docs] def to_therm_xml_str(self): """Get an THERM XML string of the material.""" xml_root = self.to_therm_xml() try: # try to indent the XML to make it read-able ET.indent(xml_root) return ET.tostring(xml_root, encoding='unicode') except AttributeError: # we are in Python 2 and no indent is available return ET.tostring(xml_root)
[docs] def to_dict(self): """MeshControl dictionary representation.""" base = { 'type': 'MeshControl', 'mesh_type': self.mesh_type, 'parameter': self.parameter, 'run_error_estimator': self.run_error_estimator, 'max_error_percent': self.max_error_percent, 'max_iterations': self.max_iterations } return base
[docs] def duplicate(self): """Get a copy of this object.""" return self.__copy__()
[docs] def ToString(self): """Overwrite .NET ToString.""" return self.__repr__()
def __key(self): """A tuple based on the object properties, useful for hashing.""" return (self.mesh_type, self.parameter, self.run_error_estimator, self.max_error_percent, self.max_iterations) def __hash__(self): return hash(self.__key()) def __eq__(self, other): return isinstance(other, MeshControl) and self.__key() == other.__key() def __ne__(self, other): return not self.__eq__(other) def __copy__(self): return self.__class__( self.mesh_type, self.parameter, self.run_error_estimator, self.max_error_percent, self.max_iterations) def __repr__(self): return 'MeshControl: {} - Parameter {}'.format(self.mesh_type, self.parameter)