Files
create/src/Mod/Fem/femsolver/elmer/writer.py
Uwe c4097ede8f [FEM] Elmer: fix default for heat
- revert change from comm it commit 117d789e - "None" is the correct default. (I mixed this up with Navier-Stokes)
2022-08-07 02:45:08 +02:00

1564 lines
65 KiB
Python

# ***************************************************************************
# * Copyright (c) 2017 Markus Hovorka <m.hovorka@live.de> *
# * Copyright (c) 2020 Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
__title__ = "FreeCAD FEM solver Elmer writer"
__author__ = "Markus Hovorka"
__url__ = "https://www.freecadweb.org"
## \addtogroup FEM
# @{
import os
import os.path
import subprocess
import tempfile
from platform import system
from FreeCAD import Console
from FreeCAD import Units
from FreeCAD import ParamGet
import Fem
from . import sifio
from . import solver as solverClass
from .. import settings
from femmesh import gmshtools
from femtools import constants
from femtools import femutils
from femtools import membertools
from .equations import elasticity
from .equations import flow
from .equations import heat
_STARTINFO_NAME = "ELMERSOLVER_STARTINFO"
_SIF_NAME = "case.sif"
_ELMERGRID_IFORMAT = "8"
_ELMERGRID_OFORMAT = "2"
_SOLID_PREFIX = "Solid"
def _getAllSubObjects(obj):
s = ["Solid" + str(i + 1) for i in range(len(obj.Shape.Solids))]
s.extend(("Face" + str(i + 1) for i in range(len(obj.Shape.Faces))))
s.extend(("Edge" + str(i + 1) for i in range(len(obj.Shape.Edges))))
s.extend(("Vertex" + str(i + 1) for i in range(len(obj.Shape.Vertexes))))
return s
class Writer(object):
def __init__(self, solver, directory, testmode=False):
self.analysis = solver.getParentGroup()
self.solver = solver
self.directory = directory
Console.PrintMessage("Write elmer input files to: {}\n".format(self.directory))
self.testmode = testmode
self._usedVarNames = set()
self._builder = sifio.Builder()
self._handledObjects = set()
self._handleUnits()
self._handleConstants()
def getHandledConstraints(self):
return self._handledObjects
def write_solver_input(self):
self._handleRedifinedConstants()
self._handleSimulation()
self._handleHeat()
self._handleElasticity()
self._handleElectrostatic()
self._handleFlux()
self._handleElectricforce()
self._handleFlow()
self._addOutputSolver()
self._writeSif()
self._writeMesh()
self._writeStartinfo()
def _handleUnits(self):
# Elmer solver writer no longer uses FreeCAD unit system
# to retrieve units for writing the sif file
#
# ATM Elmer writer uses SI units only
#
# see forum topic: https://forum.freecadweb.org/viewtopic.php?f=18&t=70150
#
# TODO: adapt method and comment
# should be only one system for all solver and not in each solver
# https://forum.freecadweb.org/viewtopic.php?t=47895
# https://forum.freecadweb.org/viewtopic.php?t=48451
# https://forum.freecadweb.org/viewtopic.php?f=10&t=48642
# The FreeCAD unit schema is only used to determine the schema number
# all definition are done here ATM
# keep in mind a unit schema might not be consistent:
# Length could be mm and Area could be m2 and Volume could be cm3
# as long as only the seven base units are retrieved from a unit schema
# the units are consistent
# TODO retrieve the seven base units from FreeCAD unit schema
# instead of hard coding them here for a second once
self.unit_schema = Units.Scheme.SI1
self.unit_system = { # standard FreeCAD Base units = unit schema 0
"L": "m",
"M": "kg",
"T": "s",
"I": "A",
"O": "K",
"N": "mol",
"J": "cd",
}
param = ParamGet("User parameter:BaseApp/Preferences/Units")
self.unit_schema = param.GetInt("UserSchema", Units.Scheme.SI1)
if self.unit_schema == Units.Scheme.SI1:
Console.PrintMessage(
"The FreeCAD standard unit schema mm/kg/s is used. "
"Elmer sif-file writing is however done in SI units.\n"
)
elif self.unit_schema == Units.Scheme.SI2:
Console.PrintMessage(
"The SI unit schema m/kg/s is used. "
"Elmer sif-file writing is done in SI-units.\n"
)
self.unit_system = {
"L": "m",
"M": "kg",
"T": "s",
"I": "A",
"O": "K",
"N": "mol",
"J": "cd",
}
elif self.unit_schema == Units.Scheme.FemMilliMeterNewton:
# see also unit comment in calculix writer
Console.PrintMessage(
"The FEM unit schema mm/N/s is used. "
"Elmer sif-file writing is however done in SI units.\n"
)
self.unit_system = {
"L": "m",
"M": "kg",
"T": "s",
"I": "A",
"O": "K",
"N": "mol",
"J": "cd",
}
elif (
self.unit_schema > Units.Scheme.SI2
and self.unit_schema != Units.Scheme.FemMilliMeterNewton
):
Console.PrintMessage(
"Unit schema: {} not supported by Elmer writer. "
"The FreeCAD standard unit schema mm/kg/s is used. "
"Elmer sif-file writing is done in Standard FreeCAD units.\n"
.format(Units.listSchemas(self.unit_schema))
)
def _getFromUi(self, value, unit, outputDim):
quantity = Units.Quantity(str(value) + str(unit))
return self._convert(quantity, outputDim)
def _convert(self, quantityStr, unit):
quantity = Units.Quantity(quantityStr)
for key, setting in self.unit_system.items():
unit = unit.replace(key, setting)
return float(quantity.getValueAs(unit))
def _handleConstants(self):
self.constsdef = {
"Gravity": constants.gravity(),
"StefanBoltzmann": constants.stefan_boltzmann(),
"PermittivityOfVacuum": constants.vacuum_permittivity(),
"BoltzmannConstant": constants.boltzmann_constant(),
}
def _getConstant(self, name, unit_dimension):
# TODO without method directly use self.constsdef[name]
return self._convert(self.constsdef[name], unit_dimension)
def _setConstant(self, name, quantityStr):
# TODO without method directly use self.constsdef[name]
if name == "PermittivityOfVacuum":
theUnit = "s^4*A^2 / (m^3*kg)"
self.constsdef[name] = "{} {}".format(self._convert(quantityStr, theUnit), theUnit)
return True
def _writeMesh(self):
mesh = self._getSingleMember("Fem::FemMeshObject")
unvPath = os.path.join(self.directory, "mesh.unv")
groups = []
groups.extend(self._builder.getBodyNames())
groups.extend(self._builder.getBoundaryNames())
self._exportToUnv(groups, mesh, unvPath)
if self.testmode:
Console.PrintMessage(
"Solver Elmer testmode, ElmerGrid will not be used. "
"It might not be installed.\n"
)
else:
binary = settings.get_binary("ElmerGrid")
num_cores = settings.get_cores("ElmerGrid")
if binary is None:
raise WriteError("Could not find ElmerGrid binary.")
# for multithreading we first need a normal mesh creation run
# then a second to split the mesh into the number of used cores
argsBasic = [binary,
_ELMERGRID_IFORMAT,
_ELMERGRID_OFORMAT,
unvPath]
args = argsBasic
args.extend(["-out", self.directory])
if system() == "Windows":
subprocess.call(
args,
stdout=subprocess.DEVNULL,
startupinfo=femutils.startProgramInfo("hide")
)
else:
subprocess.call(args, stdout=subprocess.DEVNULL)
if int(num_cores) > 1:
args = argsBasic
args.extend(["-partdual", "-metiskway", num_cores,
"-out", self.directory])
if system() == "Windows":
subprocess.call(
args,
stdout=subprocess.DEVNULL,
startupinfo=femutils.startProgramInfo("hide")
)
else:
subprocess.call(args, stdout=subprocess.DEVNULL)
def _writeStartinfo(self):
path = os.path.join(self.directory, _STARTINFO_NAME)
with open(path, "w") as f:
f.write(_SIF_NAME)
def _exportToUnv(self, groups, mesh, meshPath):
unvGmshFd, unvGmshPath = tempfile.mkstemp(suffix=".unv")
brepFd, brepPath = tempfile.mkstemp(suffix=".brep")
geoFd, geoPath = tempfile.mkstemp(suffix=".geo")
os.close(brepFd)
os.close(geoFd)
os.close(unvGmshFd)
tools = gmshtools.GmshTools(mesh)
tools.group_elements = {g: [g] for g in groups}
tools.group_nodes_export = False
tools.ele_length_map = {}
tools.temp_file_geometry = brepPath
tools.temp_file_geo = geoPath
tools.temp_file_mesh = unvGmshPath
tools.get_dimension()
tools.get_region_data()
tools.get_boundary_layer_data()
tools.write_part_file()
tools.write_geo()
if self.testmode:
Console.PrintMessage(
"Solver Elmer testmode, Gmsh will not be used. "
"It might not be installed.\n"
)
import shutil
shutil.copyfile(geoPath, os.path.join(self.directory, "group_mesh.geo"))
else:
tools.get_gmsh_command()
tools.run_gmsh_with_geo()
ioMesh = Fem.FemMesh()
ioMesh.read(unvGmshPath)
ioMesh.write(meshPath)
os.remove(brepPath)
os.remove(geoPath)
os.remove(unvGmshPath)
def _handleRedifinedConstants(self):
"""
redefine constants in self.constsdef according constant redefine objects
"""
permittivity_objs = self._getMember("Fem::ConstantVacuumPermittivity")
if len(permittivity_objs) == 1:
Console.PrintLog("Constand permittivity overwriting.\n")
self._setConstant("PermittivityOfVacuum", permittivity_objs[0].VacuumPermittivity)
elif len(permittivity_objs) > 1:
Console.PrintError(
"More than one permittivity constant overwriting objects ({} objs). "
"The permittivity constant overwriting is ignored.\n"
.format(len(permittivity_objs))
)
def _handleSimulation(self):
# check if we need to update the equation
self._updateSimulation(self.solver)
# output the equation parameters
# first check what equations we have
hasHeat = False
for equation in self.solver.Group:
if femutils.is_of_type(equation, "Fem::EquationElmerHeat"):
hasHeat = True
if hasHeat:
self._simulation("BDF Order", self.solver.BDFOrder)
self._simulation("Coordinate System", "Cartesian 3D")
self._simulation("Coordinate Mapping", (1, 2, 3))
# Elmer uses SI base units, but our mesh is in mm, therefore we must tell
# the solver that we have another scale
self._simulation("Coordinate Scaling", 0.001)
self._simulation("Output Intervals", 1)
self._simulation("Simulation Type", self.solver.SimulationType)
if self.solver.SimulationType == "Steady State":
self._simulation(
"Steady State Max Iterations",
self.solver.SteadyStateMaxIterations
)
self._simulation(
"Steady State Min Iterations",
self.solver.SteadyStateMinIterations
)
if (
self.solver.SimulationType == "Scanning"
or self.solver.SimulationType == "Transient"
):
self._simulation("Timestep Intervals", self.solver.TimestepIntervals)
self._simulation("Timestep Sizes", self.solver.TimestepSizes)
# Output Intervals must be equal to Timestep Intervals
self._simulation("Output Intervals", self.solver.TimestepIntervals)
if hasHeat:
self._simulation("Timestepping Method", "BDF")
self._simulation("Use Mesh Names", True)
def _updateSimulation(self, solver):
# updates older simulations
if not hasattr(self.solver, "BDFOrder"):
solver.addProperty(
"App::PropertyIntegerConstraint",
"BDFOrder",
"Timestepping",
"Order of time stepping method 'BDF'"
)
solver.BDFOrder = (2, 1, 5, 1)
if not hasattr(self.solver, "SimulationType"):
solver.addProperty(
"App::PropertyEnumeration",
"SimulationType",
"Type",
""
)
solver.SimulationType = solverClass.SIMULATION_TYPE
solver.SimulationType = "Steady State"
if not hasattr(self.solver, "TimestepIntervals"):
solver.addProperty(
"App::PropertyIntegerList",
"TimestepIntervals",
"Timestepping",
(
"List of maximum optimization rounds if 'Simulation Type'\n"
"is either 'Scanning' or 'Transient'"
)
)
solver.TimestepIntervals = [100]
if not hasattr(self.solver, "TimestepSizes"):
solver.addProperty(
"App::PropertyFloatList",
"TimestepSizes",
"Timestepping",
(
"List of time steps of optimization if 'Simulation Type'\n"
"is either 'Scanning' or 'Transient'"
)
)
solver.TimestepSizes = [0.1]
def _handleHeat(self):
activeIn = []
for equation in self.solver.Group:
if femutils.is_of_type(equation, "Fem::EquationElmerHeat"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getHeatSolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
self._handleHeatEquation(activeIn, equation)
if activeIn:
self._handleHeatConstants()
self._handleHeatBndConditions()
self._handleHeatInitial(activeIn)
self._handleHeatBodyForces(activeIn)
self._handleHeatMaterial(activeIn)
def _getHeatSolver(self, equation):
# check if we need to update the equation
self._updateHeatSolver(equation)
# output the equation parameters
s = self._createNonlinearSolver(equation)
s["Equation"] = equation.Name
s["Procedure"] = sifio.FileAttr("HeatSolve/HeatSolver")
s["Bubbles"] = equation.Bubbles
s["Exec Solver"] = "Always"
s["Optimize Bandwidth"] = True
s["Stabilize"] = equation.Stabilize
s["Variable"] = self._getUniqueVarName("Temperature")
return s
def _handleHeatConstants(self):
self._constant(
"Stefan Boltzmann",
self._getConstant("StefanBoltzmann", "M/(O^4*T^3)"))
def _handleHeatEquation(self, bodies, equation):
for b in bodies:
if equation.Convection != "None":
self._equation(b, "Convection", equation.Convection)
if equation.PhaseChangeModel != "None":
self._equation(b, "Phase Change Model", equation.PhaseChangeModel)
def _updateHeatSolver(self, equation):
# updates older Heat equations
if not hasattr(equation, "Convection"):
equation.addProperty(
"App::PropertyEnumeration",
"Convection",
"Equation",
"Type of convection to be used"
)
equation.Convection = heat.CONVECTION_TYPE
equation.Convection = "None"
if not hasattr(equation, "PhaseChangeModel"):
equation.addProperty(
"App::PropertyEnumeration",
"PhaseChangeModel",
"Equation",
"Model for phase change"
)
equation.PhaseChangeModel = heat.PHASE_CHANGE_MODEL
equation.PhaseChangeModel = "None"
def _handleHeatBndConditions(self):
for obj in self._getMember("Fem::ConstraintTemperature"):
if obj.References:
for name in obj.References[0][1]:
if obj.ConstraintType == "Temperature":
temp = self._getFromUi(obj.Temperature, "K", "O")
self._boundary(name, "Temperature", temp)
elif obj.ConstraintType == "CFlux":
flux = self._getFromUi(obj.CFlux, "kg*mm^2*s^-3", "M*L^2*T^-3")
self._boundary(name, "Temperature Load", flux)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintHeatflux"):
if obj.References:
for name in obj.References[0][1]:
if obj.ConstraintType == "Convection":
film = self._getFromUi(obj.FilmCoef, "W/(m^2*K)", "M/(T^3*O)")
temp = self._getFromUi(obj.AmbientTemp, "K", "O")
self._boundary(name, "Heat Transfer Coefficient", film)
self._boundary(name, "External Temperature", temp)
elif obj.ConstraintType == "DFlux":
flux = self._getFromUi(obj.DFlux, "W/m^2", "M*T^-3")
self._boundary(name, "Heat Flux BC", True)
self._boundary(name, "Heat Flux", flux)
self._handled(obj)
def _handleHeatInitial(self, bodies):
obj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if obj is not None:
for name in bodies:
temp = self._getFromUi(obj.initialTemperature, "K", "O")
self._initial(name, "Temperature", temp)
self._handled(obj)
def _handleHeatBodyForces(self, bodies):
obj = self._getSingleMember("Fem::ConstraintBodyHeatSource")
if obj is not None:
for name in bodies:
heatSource = self._getFromUi(obj.HeatSource, "W/kg", "L^2*T^-3")
# according Elmer forum W/kg is correct
# http://www.elmerfem.org/forum/viewtopic.php?f=7&t=1765
# 1 watt = kg * m2 / s3 ... W/kg = m2 / s3
self._bodyForce(name, "Heat Source", heatSource)
self._handled(obj)
def _handleHeatMaterial(self, bodies):
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = self._getFromUi(tempObj.initialTemperature, "K", "O")
for name in bodies:
self._material(name, "Reference Temperature", refTemp)
for obj in self._getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self._getAllBodies())
for name in (n for n in refs if n in bodies):
if "Density" not in m:
raise WriteError(
"Used material does not specify the necessary 'Density'."
)
self._material(
name, "Density",
self._getDensity(m))
if "ThermalConductivity" not in m:
raise WriteError(
"Used material does not specify the necessary 'Thermal Conductivity'."
)
self._material(
name, "Heat Conductivity",
self._convert(m["ThermalConductivity"], "M*L/(T^3*O)"))
if "SpecificHeat" not in m:
raise WriteError(
"Used material does not specify the necessary 'Specific Heat'."
)
self._material(
name, "Heat Capacity",
self._convert(m["SpecificHeat"], "L^2/(T^2*O)"))
def _handleElectrostatic(self):
activeIn = []
for equation in self.solver.Group:
if femutils.is_of_type(equation, "Fem::EquationElmerElectrostatic"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getElectrostaticSolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
if activeIn:
self._handleElectrostaticConstants()
self._handleElectrostaticBndConditions()
# self._handleElectrostaticInitial(activeIn)
# self._handleElectrostaticBodyForces(activeIn)
self._handleElectrostaticMaterial(activeIn)
def _getElectrostaticSolver(self, equation):
# check if we need to update the equation
self._updateElectrostaticSolver(equation)
# output the equation parameters
s = self._createLinearSolver(equation)
s["Equation"] = "Stat Elec Solver" # equation.Name
s["Procedure"] = sifio.FileAttr("StatElecSolve/StatElecSolver")
s["Variable"] = self._getUniqueVarName("Potential")
s["Variable DOFs"] = 1
if equation.CalculateCapacitanceMatrix is True:
s["Calculate Capacitance Matrix"] = equation.CalculateCapacitanceMatrix
s["Capacitance Matrix Filename"] = equation.CapacitanceMatrixFilename
if equation.CalculateElectricEnergy is True:
s["Calculate Electric Energy"] = equation.CalculateElectricEnergy
if equation.CalculateElectricField is True:
s["Calculate Electric Field"] = equation.CalculateElectricField
if equation.CalculateElectricFlux is True:
s["Calculate Electric Flux"] = equation.CalculateElectricFlux
if equation.CalculateSurfaceCharge is True:
s["Calculate Surface Charge"] = equation.CalculateSurfaceCharge
if equation.ConstantWeights is True:
s["Constant Weights"] = equation.ConstantWeights
s["Exec Solver"] = "Always"
s["Optimize Bandwidth"] = True
if (
equation.CalculateCapacitanceMatrix is False
and (equation.PotentialDifference != 0.0)
):
s["Potential Difference"] = equation.PotentialDifference
s["Stabilize"] = equation.Stabilize
return s
def _updateElectrostaticSolver(self, equation):
# updates older Electrostatic equations
if not hasattr(equation, "CapacitanceMatrixFilename"):
equation.addProperty(
"App::PropertyFile",
"CapacitanceMatrixFilename",
"Electrostatic",
(
"File where capacitance matrix is being saved\n"
"Only used if 'CalculateCapacitanceMatrix' is true"
)
)
equation.CapacitanceMatrixFilename = "cmatrix.dat"
if not hasattr(equation, "ConstantWeights"):
equation.addProperty(
"App::PropertyBool",
"ConstantWeights",
"Electrostatic",
"Use constant weighting for results"
)
if not hasattr(equation, "PotentialDifference"):
equation.addProperty(
"App::PropertyFloat",
"PotentialDifference",
"Electrostatic",
(
"Potential difference in Volt for which capacitance is\n"
"calculated if 'CalculateCapacitanceMatrix' is false"
)
)
equation.PotentialDifference = 0.0
def _handleElectrostaticConstants(self):
self._constant(
"Permittivity Of Vacuum",
self._getConstant("PermittivityOfVacuum", "T^4*I^2/(L^3*M)")
)
# https://forum.freecadweb.org/viewtopic.php?f=18&p=400959#p400959
def _handleElectrostaticMaterial(self, bodies):
for obj in self._getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self._getAllBodies())
for name in (n for n in refs if n in bodies):
if "RelativePermittivity" in m:
self._material(
name, "Relative Permittivity",
float(m["RelativePermittivity"])
)
def _handleElectrostaticBndConditions(self):
for obj in self._getMember("Fem::ConstraintElectrostaticPotential"):
if obj.References:
for name in obj.References[0][1]:
if obj.PotentialEnabled:
if hasattr(obj, "Potential"):
potential = float(obj.Potential.getValueAs("V"))
self._boundary(name, "Potential", potential)
if obj.PotentialConstant:
self._boundary(name, "Potential Constant", True)
if obj.ElectricInfinity:
self._boundary(name, "Electric Infinity BC", True)
if obj.ElectricForcecalculation:
self._boundary(name, "Calculate Electric Force", True)
if obj.CapacitanceBodyEnabled:
if hasattr(obj, "CapacitanceBody"):
self._boundary(name, "Capacitance Body", obj.CapacitanceBody)
self._handled(obj)
def _handleFlux(self):
activeIn = []
for equation in self.solver.Group:
if femutils.is_of_type(equation, "Fem::EquationElmerFlux"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getFluxSolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
def _getFluxSolver(self, equation):
s = self._createLinearSolver(equation)
# check if we need to update the equation
self._updateFluxSolver(equation)
# output the equation parameters
s["Equation"] = "Flux Solver" # equation.Name
s["Procedure"] = sifio.FileAttr("FluxSolver/FluxSolver")
if equation.AverageWithinMaterials is True:
s["Average Within Materials"] = equation.AverageWithinMaterials
s["Calculate Flux"] = equation.CalculateFlux
if equation.CalculateFluxAbs is True:
s["Calculate Flux Abs"] = equation.CalculateFluxAbs
if equation.CalculateFluxMagnitude is True:
s["Calculate Flux Magnitude"] = equation.CalculateFluxMagnitude
s["Calculate Grad"] = equation.CalculateGrad
if equation.CalculateGradAbs is True:
s["Calculate Grad Abs"] = equation.CalculateGradAbs
if equation.CalculateGradMagnitude is True:
s["Calculate Grad Magnitude"] = equation.CalculateGradMagnitude
if equation.DiscontinuousGalerkin is True:
s["Discontinuous Galerkin"] = equation.DiscontinuousGalerkin
if equation.EnforcePositiveMagnitude is True:
s["Enforce Positive Magnitude"] = equation.EnforcePositiveMagnitude
s["Flux Coefficient"] = equation.FluxCoefficient
s["Flux Variable"] = equation.FluxVariable
s["Stabilize"] = equation.Stabilize
return s
def _updateFluxSolver(self, equation):
# updates older Flux equations
if not hasattr(equation, "AverageWithinMaterials"):
equation.addProperty(
"App::PropertyBool",
"AverageWithinMaterials",
"Flux",
(
"Enforces continuity within the same material\n"
"in the 'Discontinuous Galerkin' discretization"
)
)
if hasattr(equation, "Bubbles"):
# Bubbles was removed because it is unused by Elmer for the flux solver
equation.removeProperty("Bubbles")
if not hasattr(equation, "CalculateFluxAbs"):
equation.addProperty(
"App::PropertyBool",
"CalculateFluxAbs",
"Flux",
"Computes absolute of flux vector"
)
if not hasattr(equation, "CalculateFluxMagnitude"):
equation.addProperty(
"App::PropertyBool",
"CalculateFluxMagnitude",
"Flux",
"Computes magnitude of flux vector field"
)
if not hasattr(equation, "CalculateGradAbs"):
equation.addProperty(
"App::PropertyBool",
"CalculateGradAbs",
"Flux",
"Computes absolute of gradient field"
)
if not hasattr(equation, "CalculateGradMagnitude"):
equation.addProperty(
"App::PropertyBool",
"CalculateGradMagnitude",
"Flux",
"Computes magnitude of gradient field"
)
if not hasattr(equation, "DiscontinuousGalerkin"):
equation.addProperty(
"App::PropertyBool",
"DiscontinuousGalerkin",
"Flux",
(
"Enable if standard Galerkin approximation leads to\n"
"unphysical results when there are discontinuities"
)
)
if not hasattr(equation, "EnforcePositiveMagnitude"):
equation.addProperty(
"App::PropertyBool",
"EnforcePositiveMagnitude",
"Flux",
(
"If true, negative values of computed magnitude fields\n"
"are a posteriori set to zero."
)
)
if not hasattr(equation, "FluxCoefficient"):
equation.addProperty(
"App::PropertyString",
"FluxCoefficient",
"Flux",
"Name of proportionality coefficient\nto compute the flux"
)
def _handleElectricforce(self):
activeIn = []
for equation in self.solver.Group:
if femutils.is_of_type(equation, "Fem::EquationElmerElectricforce"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getElectricforceSolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
def _getElectricforceSolver(self, equation):
s = self._createEmptySolver()
s["Equation"] = "Electric Force" # equation.Name
s["Procedure"] = sifio.FileAttr("ElectricForce/StatElecForce")
s["Stabilize"] = equation.Stabilize
return s
def _handleElasticity(self):
activeIn = []
for equation in self.solver.Group:
if femutils.is_of_type(equation, "Fem::EquationElmerElasticity"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getElasticitySolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
self._handleElasticityEquation(activeIn, equation)
if activeIn:
self._handleElasticityConstants()
self._handleElasticityBndConditions()
self._handleElasticityInitial(activeIn)
self._handleElasticityBodyForces(activeIn)
self._handleElasticityMaterial(activeIn)
def _getElasticitySolver(self, equation):
s = self._createLinearSolver(equation)
# check if we need to update the equation
self._updateElasticitySolver(equation)
# output the equation parameters
s["Equation"] = "Stress Solver" # equation.Name
s["Procedure"] = sifio.FileAttr("StressSolve/StressSolver")
if equation.CalculateStrains is True:
s["Calculate Strains"] = equation.CalculateStrains
if equation.CalculateStresses is True:
s["Calculate Stresses"] = equation.CalculateStresses
if equation.CalculatePrincipal is True:
s["Calculate Principal"] = equation.CalculatePrincipal
if equation.CalculatePangle is True:
s["Calculate Pangle"] = equation.CalculatePangle
if equation.ConstantBulkSystem is True:
s["Constant Bulk System"] = equation.ConstantBulkSystem
s["Displace mesh"] = equation.DisplaceMesh
s["Eigen Analysis"] = equation.EigenAnalysis
if equation.EigenAnalysis is True:
s["Eigen System Convergence Tolerance"] = \
equation.EigenSystemTolerance
s["Eigen System Complex"] = equation.EigenSystemComplex
if equation.EigenSystemComputeResiduals is True:
s["Eigen System Compute Residuals"] = equation.EigenSystemComputeResiduals
s["Eigen System Damped"] = equation.EigenSystemDamped
s["Eigen System Max Iterations"] = equation.EigenSystemMaxIterations
s["Eigen System Select"] = equation.EigenSystemSelect
s["Eigen System Values"] = equation.EigenSystemValues
if equation.FixDisplacement is True:
s["Fix Displacement"] = equation.FixDisplacement
s["Geometric Stiffness"] = equation.GeometricStiffness
if equation.Incompressible is True:
s["Incompressible"] = equation.Incompressible
if equation.MaxwellMaterial is True:
s["Maxwell Material"] = equation.MaxwellMaterial
if equation.ModelLumping is True:
s["Model Lumping"] = equation.ModelLumping
if equation.ModelLumping is True:
s["Model Lumping Filename"] = equation.ModelLumpingFilename
s["Optimize Bandwidth"] = True
if equation.StabilityAnalysis is True:
s["Stability Analysis"] = equation.StabilityAnalysis
s["Stabilize"] = equation.Stabilize
if equation.UpdateTransientSystem is True:
s["Update Transient System"] = equation.UpdateTransientSystem
s["Variable"] = equation.Variable
s["Variable DOFs"] = 3
return s
def _handleElasticityEquation(self, bodies, equation):
for b in bodies:
if equation.PlaneStress:
self._equation(b, "Plane Stress", equation.PlaneStress)
def _updateElasticitySolver(self, equation):
# updates older Elasticity equations
if not hasattr(equation, "Variable"):
equation.addProperty(
"App::PropertyString",
"Variable",
"Elasticity",
(
"Only change this if 'Incompressible' is set to true\n"
"according to the Elmer manual."
)
)
equation.Variable = "Displacement"
if hasattr(equation, "Bubbles"):
# Bubbles was removed because it is unused by Elmer for the stress solver
equation.removeProperty("Bubbles")
if not hasattr(equation, "ConstantBulkSystem"):
equation.addProperty(
"App::PropertyBool",
"ConstantBulkSystem",
"Elasticity",
"See Elmer manual for info"
)
if not hasattr(equation, "DisplaceMesh"):
equation.addProperty(
"App::PropertyBool",
"DisplaceMesh",
"Elasticity",
(
"If mesh is deformed by displacement field.\n"
"Set to False for 'Eigen Analysis'."
)
)
# DisplaceMesh is true except if DoFrequencyAnalysis is true
equation.DisplaceMesh = True
if hasattr(equation, "DoFrequencyAnalysis"):
if equation.DoFrequencyAnalysis is True:
equation.DisplaceMesh = False
if not hasattr(equation, "EigenAnalysis"):
# DoFrequencyAnalysis was renamed to EigenAnalysis
# to follow the Elmer manual
equation.addProperty(
"App::PropertyBool",
"EigenAnalysis",
"Eigen Values",
"If true, modal analysis"
)
if hasattr(equation, "DoFrequencyAnalysis"):
equation.EigenAnalysis = equation.DoFrequencyAnalysis
equation.removeProperty("DoFrequencyAnalysis")
if not hasattr(equation, "EigenSystemComplex"):
equation.addProperty(
"App::PropertyBool",
"EigenSystemComplex",
"Eigen Values",
(
"Should be true if eigen system is complex\n"
"Must be false for a damped eigen value analysis."
)
)
equation.EigenSystemComplex = True
if not hasattr(equation, "EigenSystemComputeResiduals"):
equation.addProperty(
"App::PropertyBool",
"EigenSystemComputeResiduals",
"Eigen Values",
"Computes residuals of eigen value system"
)
if not hasattr(equation, "EigenSystemDamped"):
equation.addProperty(
"App::PropertyBool",
"EigenSystemDamped",
"Eigen Values",
(
"Set a damped eigen analysis. Can only be\n"
"used if 'Linear Solver Type' is 'Iterative'."
)
)
if not hasattr(equation, "EigenSystemMaxIterations"):
equation.addProperty(
"App::PropertyIntegerConstraint",
"EigenSystemMaxIterations",
"Eigen Values",
"Max iterations for iterative eigensystem solver"
)
equation.EigenSystemMaxIterations = (300, 1, int(1e8), 1)
if not hasattr(equation, "EigenSystemSelect"):
equation.addProperty(
"App::PropertyEnumeration",
"EigenSystemSelect",
"Eigen Values",
"Which eigenvalues are computed"
)
equation.EigenSystemSelect = elasticity.EIGEN_SYSTEM_SELECT
equation.EigenSystemSelect = "Smallest Magnitude"
if not hasattr(equation, "EigenSystemTolerance"):
equation.addProperty(
"App::PropertyFloat",
"EigenSystemTolerance",
"Eigen Values",
(
"Convergence tolerance for iterative eigensystem solve\n"
"Default is 100 times the 'Linear Tolerance'"
)
)
equation.setExpression("EigenSystemTolerance", str(100 * equation.LinearTolerance))
if not hasattr(equation, "EigenSystemValues"):
# EigenmodesCount was renamed to EigenSystemValues
# to follow the Elmer manual
equation.addProperty(
"App::PropertyInteger",
"EigenSystemValues",
"Eigen Values",
"Number of lowest eigen modes"
)
if hasattr(equation, "EigenmodesCount"):
equation.EigenSystemValues = equation.EigenmodesCount
equation.removeProperty("EigenmodesCount")
if not hasattr(equation, "FixDisplacement"):
equation.addProperty(
"App::PropertyBool",
"FixDisplacement",
"Elasticity",
"If displacements or forces are set,\nthereby model lumping is used"
)
if not hasattr(equation, "GeometricStiffness"):
equation.addProperty(
"App::PropertyBool",
"GeometricStiffness",
"Elasticity",
"Consider geometric stiffness"
)
if not hasattr(equation, "Incompressible"):
equation.addProperty(
"App::PropertyBool",
"Incompressible",
"Elasticity",
(
"Computation of incompressible material in connection\n"
"with viscoelastic Maxwell material and a custom 'Variable'"
)
)
if not hasattr(equation, "MaxwellMaterial"):
equation.addProperty(
"App::PropertyBool",
"MaxwellMaterial",
"Elasticity",
"Compute viscoelastic material model"
)
if not hasattr(equation, "ModelLumping"):
equation.addProperty(
"App::PropertyBool",
"ModelLumping",
"Elasticity",
"Use model lumping"
)
if not hasattr(equation, "ModelLumpingFilename"):
equation.addProperty(
"App::PropertyFile",
"ModelLumpingFilename",
"Elasticity",
"File to save results from model lumping to"
)
if not hasattr(equation, "PlaneStress"):
equation.addProperty(
"App::PropertyBool",
"PlaneStress",
"Equation",
(
"Computes solution according to plane\nstress situation.\n"
"Applies only for 2D geometry."
)
)
if not hasattr(equation, "StabilityAnalysis"):
equation.addProperty(
"App::PropertyBool",
"StabilityAnalysis",
"Elasticity",
(
"If true, 'Eigen Analysis' is stability analysis.\n"
"Otherwise modal analysis is performed."
)
)
if not hasattr(equation, "UpdateTransientSystem"):
equation.addProperty(
"App::PropertyBool",
"UpdateTransientSystem",
"Elasticity",
"See Elmer manual for info"
)
def _handleElasticityConstants(self):
pass
def _handleElasticityBndConditions(self):
for obj in self._getMember("Fem::ConstraintPressure"):
if obj.References:
for name in obj.References[0][1]:
pressure = self._getFromUi(obj.Pressure, "MPa", "M/(L*T^2)")
if not obj.Reversed:
pressure *= -1
self._boundary(name, "Normal Force", pressure)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintFixed"):
if obj.References:
for name in obj.References[0][1]:
self._boundary(name, "Displacement 1", 0.0)
self._boundary(name, "Displacement 2", 0.0)
self._boundary(name, "Displacement 3", 0.0)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintForce"):
if obj.References:
for name in obj.References[0][1]:
force = self._getFromUi(obj.Force, "N", "M*L*T^-2")
self._boundary(name, "Force 1", obj.DirectionVector.x * force)
self._boundary(name, "Force 2", obj.DirectionVector.y * force)
self._boundary(name, "Force 3", obj.DirectionVector.z * force)
self._boundary(name, "Force 1 Normalize by Area", True)
self._boundary(name, "Force 2 Normalize by Area", True)
self._boundary(name, "Force 3 Normalize by Area", True)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintDisplacement"):
if obj.References:
for name in obj.References[0][1]:
if not obj.xFree:
self._boundary(
name, "Displacement 1", obj.xDisplacement * 0.001)
elif obj.xFix:
self._boundary(name, "Displacement 1", 0.0)
if not obj.yFree:
self._boundary(
name, "Displacement 2", obj.yDisplacement * 0.001)
elif obj.yFix:
self._boundary(name, "Displacement 2", 0.0)
if not obj.zFree:
self._boundary(
name, "Displacement 3", obj.zDisplacement * 0.001)
elif obj.zFix:
self._boundary(name, "Displacement 3", 0.0)
self._handled(obj)
def _handleElasticityInitial(self, bodies):
pass
def _handleElasticityBodyForces(self, bodies):
obj = self._getSingleMember("Fem::ConstraintSelfWeight")
if obj is not None:
for name in bodies:
gravity = self._getConstant("Gravity", "L/T^2")
m = self._getBodyMaterial(name).Material
densityQuantity = Units.Quantity(m["Density"])
dimension = "M/L^3"
if name.startswith("Edge"):
# not tested, bernd
# TODO: test
densityQuantity.Unit = Units.Unit(-2, 1)
dimension = "M/L^2"
density = self._convert(densityQuantity, dimension)
force1 = gravity * obj.Gravity_x * density
force2 = gravity * obj.Gravity_y * density
force3 = gravity * obj.Gravity_z * density
self._bodyForce(name, "Stress Bodyforce 1", force1)
self._bodyForce(name, "Stress Bodyforce 2", force2)
self._bodyForce(name, "Stress Bodyforce 3", force3)
self._handled(obj)
def _getBodyMaterial(self, name):
for obj in self._getMember("App::MaterialObject"):
if not obj.References or name in obj.References[0][1]:
return obj
return None
def _handleElasticityMaterial(self, bodies):
# density
# is needed for self weight constraints and frequency analysis
density_needed = False
for equation in self.solver.Group:
if femutils.is_of_type(equation, "Fem::EquationElmerElasticity"):
if equation.EigenAnalysis is True:
density_needed = True
break # there could be a second equation without frequency
gravObj = self._getSingleMember("Fem::ConstraintSelfWeight")
if gravObj is not None:
density_needed = True
# temperature
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = self._getFromUi(tempObj.initialTemperature, "K", "O")
for name in bodies:
self._material(name, "Reference Temperature", refTemp)
# get the material data for all boddies
for obj in self._getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self._getAllBodies()
)
for name in (n for n in refs if n in bodies):
if density_needed is True:
self._material(
name, "Density",
self._getDensity(m)
)
self._material(
name, "Youngs Modulus",
self._getYoungsModulus(m)
)
self._material(
name, "Poisson ratio",
float(m["PoissonRatio"])
)
if tempObj:
self._material(
name, "Heat expansion Coefficient",
self._convert(m["ThermalExpansionCoefficient"], "O^-1")
)
def _getDensity(self, m):
density = self._convert(m["Density"], "M/L^3")
if self._getMeshDimension() == 2:
density *= 1e3
return density
def _getYoungsModulus(self, m):
youngsModulus = self._convert(m["YoungsModulus"], "M/(L*T^2)")
if self._getMeshDimension() == 2:
youngsModulus *= 1e3
return youngsModulus
def _isMaterialFlow(self, body):
m = self._getBodyMaterial(body).Material
return "KinematicViscosity" in m
def _handleFlow(self):
activeIn = []
for equation in self.solver.Group:
if femutils.is_of_type(equation, "Fem::EquationElmerFlow"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getFlowSolver(equation)
for body in activeIn:
if self._isMaterialFlow(body):
self._addSolver(body, solverSection)
self._handleFlowEquation(activeIn, equation)
if activeIn:
self._handleFlowConstants()
self._handleFlowBndConditions()
self._handleFlowInitialVelocity(activeIn)
# self._handleFlowInitial(activeIn)
# self._handleFlowBodyForces(activeIn)
self._handleFlowMaterial(activeIn)
def _getFlowSolver(self, equation):
# check if we need to update the equation
self._updateFlowSolver(equation)
# output the equation parameters
s = self._createNonlinearSolver(equation)
s["Equation"] = "Navier-Stokes"
s["Procedure"] = sifio.FileAttr("FlowSolve/FlowSolver")
if equation.DivDiscretization is True:
s["Div Discretization"] = equation.DivDiscretization
s["Exec Solver"] = "Always"
if equation.FlowModel != "Full":
s["Flow Model"] = equation.FlowModel
if equation.GradpDiscretization is True:
s["Gradp Discretization"] = equation.GradpDiscretization
s["Stabilize"] = equation.Stabilize
s["Optimize Bandwidth"] = True
if equation.Variable != "Flow Solution[Velocity:3 Pressure:1]":
s["Variable"] = equation.Variable
return s
def _handleFlowConstants(self):
gravity = self._getConstant("Gravity", "L/T^2")
self._constant("Gravity", (0.0, -1.0, 0.0, gravity))
def _updateFlowSolver(self, equation):
# updates older Flow equations
if not hasattr(equation, "Convection"):
equation.addProperty(
"App::PropertyEnumeration",
"Convection",
"Equation",
"Type of convection to be used"
)
equation.Convection = flow.CONVECTION_TYPE
equation.Convection = "Computed"
if not hasattr(equation, "DivDiscretization"):
equation.addProperty(
"App::PropertyBool",
"DivDiscretization",
"Flow",
(
"Set to true for incompressible flow for more stable\n"
"discretization when Reynolds number increases"
)
)
if not hasattr(equation, "FlowModel"):
equation.addProperty(
"App::PropertyEnumeration",
"FlowModel",
"Flow",
"Flow model to be used"
)
equation.FlowModel = flow.FLOW_MODEL
equation.FlowModel = "Full"
if not hasattr(equation, "GradpDiscretization"):
equation.addProperty(
"App::PropertyBool",
"GradpDiscretization",
"Flow",
(
"If true pressure Dirichlet boundary conditions can be used.\n"
"Also mass flux is available as a natural boundary condition."
)
)
if not hasattr(equation, "MagneticInduction"):
equation.addProperty(
"App::PropertyBool",
"MagneticInduction",
"Equation",
(
"Magnetic induction equation will be solved\n"
"along with the Navier-Stokes equations"
)
)
if not hasattr(equation, "Variable"):
equation.addProperty(
"App::PropertyString",
"Variable",
"Flow",
"Only for a 2D model change the '3' to '2'"
)
equation.Variable = "Flow Solution[Velocity:3 Pressure:1]"
def _handleFlowMaterial(self, bodies):
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = self._getFromUi(tempObj.initialTemperature, "K", "O")
for name in bodies:
self._material(name, "Reference Temperature", refTemp)
for obj in self._getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self._getAllBodies())
for name in (n for n in refs if n in bodies):
if "Density" in m:
self._material(
name, "Density",
self._getDensity(m)
)
if "ThermalConductivity" in m:
self._material(
name, "Heat Conductivity",
self._convert(m["ThermalConductivity"], "M*L/(T^3*O)")
)
if "KinematicViscosity" in m:
density = self._getDensity(m)
kViscosity = self._convert(m["KinematicViscosity"], "L^2/T")
self._material(
name, "Viscosity", kViscosity * density)
if "ThermalExpansionCoefficient" in m:
value = self._convert(m["ThermalExpansionCoefficient"], "O^-1")
if value > 0:
self._material(
name, "Heat expansion Coefficient", value)
if "ReferencePressure" in m:
pressure = self._convert(m["ReferencePressure"], "M/(L*T^2)")
self._material(name, "Reference Pressure", pressure)
if "SpecificHeatRatio" in m:
self._material(
name, "Specific Heat Ratio",
float(m["SpecificHeatRatio"])
)
if "CompressibilityModel" in m:
self._material(
name, "Compressibility Model",
m["CompressibilityModel"])
def _handleFlowInitialVelocity(self, bodies):
obj = self._getSingleMember("Fem::ConstraintInitialFlowVelocity")
if obj is not None:
for name in bodies:
if obj.VelocityXEnabled:
velocity = self._getFromUi(obj.VelocityX, "m/s", "L/T")
self._initial(name, "Velocity 1", velocity)
if obj.VelocityYEnabled:
velocity = self._getFromUi(obj.VelocityY, "m/s", "L/T")
self._initial(name, "Velocity 2", velocity)
if obj.VelocityZEnabled:
velocity = self._getFromUi(obj.VelocityZ, "m/s", "L/T")
self._initial(name, "Velocity 3", velocity)
self._handled(obj)
def _handleFlowBndConditions(self):
for obj in self._getMember("Fem::ConstraintFlowVelocity"):
if obj.References:
for name in obj.References[0][1]:
if obj.VelocityXEnabled:
velocity = self._getFromUi(obj.VelocityX, "m/s", "L/T")
self._boundary(name, "Velocity 1", velocity)
if obj.VelocityYEnabled:
velocity = self._getFromUi(obj.VelocityY, "m/s", "L/T")
self._boundary(name, "Velocity 2", velocity)
if obj.VelocityZEnabled:
velocity = self._getFromUi(obj.VelocityZ, "m/s", "L/T")
self._boundary(name, "Velocity 3", velocity)
if obj.NormalToBoundary:
self._boundary(name, "Normal-Tangential Velocity", True)
self._handled(obj)
def _handleFlowEquation(self, bodies, equation):
for b in bodies:
if equation.Convection != "None":
self._equation(b, "Convection", equation.Convection)
if equation.MagneticInduction is True:
self._equation(b, "Magnetic Induction", equation.MagneticInduction)
def _createEmptySolver(self):
s = sifio.createSection(sifio.SOLVER)
return s
def _hasExpression(self, equation):
obj = None
exp = None
for (obj, exp) in equation.ExpressionEngine:
if obj == equation:
return exp
return None
def _updateLinearSolver(self, equation):
if self._hasExpression(equation) != equation.LinearTolerance:
equation.setExpression("LinearTolerance", str(equation.LinearTolerance))
if self._hasExpression(equation) != equation.SteadyStateTolerance:
equation.setExpression("SteadyStateTolerance", str(equation.SteadyStateTolerance))
def _createLinearSolver(self, equation):
# first check if we have to update
self._updateLinearSolver(equation)
# write the solver
s = sifio.createSection(sifio.SOLVER)
s.priority = equation.Priority
s["Linear System Solver"] = equation.LinearSolverType
if equation.LinearSolverType == "Direct":
s["Linear System Direct Method"] = \
equation.LinearDirectMethod
elif equation.LinearSolverType == "Iterative":
s["Linear System Iterative Method"] = \
equation.LinearIterativeMethod
if equation.LinearIterativeMethod == "BiCGStabl":
s["BiCGstabl polynomial degree"] = \
equation.BiCGstablDegree
s["Linear System Max Iterations"] = \
equation.LinearIterations
s["Linear System Convergence Tolerance"] = \
equation.LinearTolerance
s["Linear System Preconditioning"] = \
equation.LinearPreconditioning
s["Steady State Convergence Tolerance"] = \
equation.SteadyStateTolerance
s["Linear System Abort Not Converged"] = False
s["Linear System Residual Output"] = 1
s["Linear System Precondition Recompute"] = 1
return s
def _updateNonlinearSolver(self, equation):
if self._hasExpression(equation) != equation.NonlinearTolerance:
equation.setExpression("NonlinearTolerance", str(equation.NonlinearTolerance))
if self._hasExpression(equation) != equation.NonlinearNewtonAfterTolerance:
equation.setExpression(
"NonlinearNewtonAfterTolerance",
str(equation.NonlinearNewtonAfterTolerance)
)
def _createNonlinearSolver(self, equation):
# first check if we have to update
self._updateNonlinearSolver(equation)
# write the linear solver
s = self._createLinearSolver(equation)
# write the nonlinear solver
s["Nonlinear System Max Iterations"] = \
equation.NonlinearIterations
s["Nonlinear System Convergence Tolerance"] = \
equation.NonlinearTolerance
s["Nonlinear System Relaxation Factor"] = \
equation.RelaxationFactor
s["Nonlinear System Newton After Iterations"] = \
equation.NonlinearNewtonAfterIterations
s["Nonlinear System Newton After Tolerance"] = \
equation.NonlinearNewtonAfterTolerance
return s
def _getUniqueVarName(self, varName):
postfix = 1
if varName in self._usedVarNames:
varName += "%2d" % postfix
while varName in self._usedVarNames:
postfix += 1
varName = varName[:-2] + "%2d" % postfix
self._usedVarNames.add(varName)
return varName
def _getAllBodies(self):
obj = self._getSingleMember("Fem::FemMeshObject")
bodyCount = 0
prefix = ""
if obj.Part.Shape.Solids:
prefix = "Solid"
bodyCount = len(obj.Part.Shape.Solids)
elif obj.Part.Shape.Faces:
prefix = "Face"
bodyCount = len(obj.Part.Shape.Faces)
elif obj.Part.Shape.Edges:
prefix = "Edge"
bodyCount = len(obj.Part.Shape.Edges)
return [prefix + str(i + 1) for i in range(bodyCount)]
def _getMeshDimension(self):
obj = self._getSingleMember("Fem::FemMeshObject")
if obj.Part.Shape.Solids:
return 3
elif obj.Part.Shape.Faces:
return 2
elif obj.Part.Shape.Edges:
return 1
return None
def _addOutputSolver(self):
s = sifio.createSection(sifio.SOLVER)
s["Equation"] = "ResultOutput"
s["Exec Solver"] = "After simulation"
s["Procedure"] = sifio.FileAttr("ResultOutputSolve/ResultOutputSolver")
s["Output File Name"] = sifio.FileAttr("case")
s["Vtu Format"] = True
if self.unit_schema == Units.Scheme.SI2:
s["Coordinate Scaling Revert"] = True
Console.PrintMessage(
"'Coordinate Scaling Revert = Logical True' was "
"inserted into the solver input file.\n"
)
for name in self._getAllBodies():
self._addSolver(name, s)
def _writeSif(self):
sifPath = os.path.join(self.directory, _SIF_NAME)
with open(sifPath, "w") as fstream:
sif = sifio.Sif(self._builder)
sif.write(fstream)
def _handled(self, obj):
self._handledObjects.add(obj)
def _simulation(self, key, attr):
self._builder.simulation(key, attr)
def _constant(self, key, attr):
self._builder.constant(key, attr)
def _initial(self, body, key, attr):
self._builder.initial(body, key, attr)
def _material(self, body, key, attr):
self._builder.material(body, key, attr)
def _equation(self, body, key, attr):
self._builder.equation(body, key, attr)
def _bodyForce(self, body, key, attr):
self._builder.bodyForce(body, key, attr)
def _addSolver(self, body, solverSection):
self._builder.addSolver(body, solverSection)
def _boundary(self, boundary, key, attr):
self._builder.boundary(boundary, key, attr)
def _addSection(self, section):
self._builder.addSection(section)
def _getMember(self, t):
return membertools.get_member(self.analysis, t)
def _getSingleMember(self, t):
return membertools.get_single_member(self.analysis, t)
class WriteError(Exception):
pass
## @}