FEM: reformat codebase

This commit is contained in:
lyphrowny
2024-06-19 14:29:15 +03:00
parent 8675aa87b4
commit 359c1ae2bb
302 changed files with 6702 additions and 9421 deletions

View File

@@ -34,8 +34,7 @@ from ... import equationbase
def create(doc, name="Deformation"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(nonlinear.Proxy, equationbase.DeformationProxy):
@@ -43,51 +42,40 @@ class Proxy(nonlinear.Proxy, equationbase.DeformationProxy):
Type = "Fem::EquationElmerDeformation"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyBool",
"CalculatePangle",
"Deformation",
"Compute principal stress angles"
"App::PropertyBool", "CalculatePangle", "Deformation", "Compute principal stress angles"
)
obj.addProperty(
"App::PropertyBool",
"CalculatePrincipal",
"Deformation",
"Compute principal stress components"
"Compute principal stress components",
)
obj.addProperty(
"App::PropertyBool",
"CalculateStrains",
"Deformation",
"Compute the strain tensor"
"App::PropertyBool", "CalculateStrains", "Deformation", "Compute the strain tensor"
)
obj.addProperty(
"App::PropertyBool",
"CalculateStresses",
"Deformation",
"Compute stress tensor and vanMises"
"Compute stress tensor and vanMises",
)
obj.addProperty(
"App::PropertyBool",
"InitializeStateVariables",
"Deformation",
"See Elmer manual for info"
"See Elmer manual for info",
)
obj.addProperty(
"App::PropertyBool",
"MixedFormulation",
"Deformation",
"See Elmer manual for info"
"App::PropertyBool", "MixedFormulation", "Deformation", "See Elmer manual for info"
)
obj.addProperty(
"App::PropertyBool",
"NeoHookeanMaterial",
"Deformation",
(
"Uses the neo-Hookean material model"
)
("Uses the neo-Hookean material model"),
)
obj.addProperty(
"App::PropertyBool",
@@ -96,13 +84,13 @@ class Proxy(nonlinear.Proxy, equationbase.DeformationProxy):
(
"Computes solution according to plane\nstress situation.\n"
"Applies only for 2D geometry."
)
),
)
obj.addProperty(
"App::PropertyString",
"Variable",
"Deformation",
"Only for a 2D model change the '3' to '2'"
"Only for a 2D model change the '3' to '2'",
)
obj.Priority = 10
@@ -114,4 +102,5 @@ class Proxy(nonlinear.Proxy, equationbase.DeformationProxy):
class ViewProxy(nonlinear.ViewProxy, equationbase.DeformationViewProxy):
pass
## @}

View File

@@ -151,7 +151,7 @@ class DeformationWriter:
gravity = self.write.convert(obj.GravityAcceleration.toStr(), "L/T^2")
if self.write.getBodyMaterial(name) is None:
raise general_writer.WriteError(
"The body {} is not referenced in any material.\n\n".format(name)
f"The body {name} is not referenced in any material.\n\n"
)
m = self.write.getBodyMaterial(name).Material
@@ -193,17 +193,13 @@ class DeformationWriter:
# get the material data for all bodies
for obj in self.write.getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self.write.getAllBodies()
)
refs = obj.References[0][1] if obj.References else self.write.getAllBodies()
for name in (n for n in refs if n in bodies):
# don't evaluate fluid material
if self.write.isBodyMaterialFluid(name):
break
if "YoungsModulus" not in m:
Console.PrintMessage("m: {}\n".format(m))
Console.PrintMessage(f"m: {m}\n")
# it is no fluid but also no solid
# -> user set no material reference at all
# that now material is known
@@ -213,22 +209,14 @@ class DeformationWriter:
)
self.write.material(name, "Name", m["Name"])
if density_needed is True:
self.write.material(
name, "Density",
self.write.getDensity(m)
)
self.write.material(
name, "Youngs Modulus",
self._getYoungsModulus(m)
)
self.write.material(
name, "Poisson ratio",
float(m["PoissonRatio"])
)
self.write.material(name, "Density", self.write.getDensity(m))
self.write.material(name, "Youngs Modulus", self._getYoungsModulus(m))
self.write.material(name, "Poisson ratio", float(m["PoissonRatio"]))
if tempObj:
self.write.material(
name, "Heat expansion Coefficient",
self.write.convert(m["ThermalExpansionCoefficient"], "O^-1")
name,
"Heat expansion Coefficient",
self.write.convert(m["ThermalExpansionCoefficient"], "O^-1"),
)
def _getYoungsModulus(self, m):
@@ -237,4 +225,5 @@ class DeformationWriter:
youngsModulus *= 1e3
return youngsModulus
## @}

View File

@@ -39,13 +39,12 @@ EIGEN_SYSTEM_SELECT = [
"Smallest Real Part",
"Largest Real Part",
"Smallest Imag Part",
"Largest Imag Part"
"Largest Imag Part",
]
def create(doc, name="Elasticity"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(linear.Proxy, equationbase.ElasticityProxy):
@@ -53,52 +52,37 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
Type = "Fem::EquationElmerElasticity"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyBool",
"CalculatePangle",
"Elasticity",
"Compute principal stress angles"
"App::PropertyBool", "CalculatePangle", "Elasticity", "Compute principal stress angles"
)
obj.addProperty(
"App::PropertyBool",
"CalculatePrincipal",
"Elasticity",
"Compute principal stress components"
"Compute principal stress components",
)
obj.addProperty(
"App::PropertyBool",
"CalculateStrains",
"Elasticity",
"Compute the strain tensor"
"App::PropertyBool", "CalculateStrains", "Elasticity", "Compute the strain tensor"
)
obj.addProperty(
"App::PropertyBool",
"CalculateStresses",
"Elasticity",
"Compute stress tensor and vanMises"
"Compute stress tensor and vanMises",
)
obj.addProperty(
"App::PropertyBool",
"ConstantBulkSystem",
"Elasticity",
"See Elmer manual for info"
"App::PropertyBool", "ConstantBulkSystem", "Elasticity", "See Elmer manual for info"
)
obj.addProperty(
"App::PropertyBool",
"DisplaceMesh",
"Elasticity",
(
"If mesh is deformed by displacement field.\n"
"Set to False for 'Eigen Analysis'."
)
("If mesh is deformed by displacement field.\nSet to False for 'Eigen Analysis'."),
)
obj.addProperty(
"App::PropertyBool",
"EigenAnalysis",
"Eigen Values",
"If true, modal analysis"
"App::PropertyBool", "EigenAnalysis", "Eigen Values", "If true, modal analysis"
)
obj.addProperty(
"App::PropertyBool",
@@ -107,13 +91,13 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
(
"Should be true if eigen system is complex\n"
"Must be false for a damped eigen value analysis."
)
),
)
obj.addProperty(
"App::PropertyBool",
"EigenSystemComputeResiduals",
"Eigen Values",
"Computes residuals of eigen value system"
"Computes residuals of eigen value system",
)
obj.addProperty(
"App::PropertyBool",
@@ -122,19 +106,19 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
(
"Set a damped eigen analysis. Can only be\n"
"used if 'Linear Solver Type' is 'Iterative'."
)
),
)
obj.addProperty(
"App::PropertyIntegerConstraint",
"EigenSystemMaxIterations",
"Eigen Values",
"Max iterations for iterative eigensystem solver"
"Max iterations for iterative eigensystem solver",
)
obj.addProperty(
"App::PropertyEnumeration",
"EigenSystemSelect",
"Eigen Values",
"Which eigenvalues are computed"
"Which eigenvalues are computed",
)
obj.addProperty(
"App::PropertyFloat",
@@ -143,25 +127,22 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
(
"Convergence tolerance for iterative eigensystem solve\n"
"Default is 100 times the 'Linear Tolerance'"
)
),
)
obj.addProperty(
"App::PropertyInteger",
"EigenSystemValues",
"Eigen Values",
"Number of lowest eigen modes"
"Number of lowest eigen modes",
)
obj.addProperty(
"App::PropertyBool",
"FixDisplacement",
"Elasticity",
"If displacements or forces are set,\nthereby model lumping is used"
"If displacements or forces are set,\nthereby model lumping is used",
)
obj.addProperty(
"App::PropertyBool",
"GeometricStiffness",
"Elasticity",
"Consider geometric stiffness"
"App::PropertyBool", "GeometricStiffness", "Elasticity", "Consider geometric stiffness"
)
obj.addProperty(
"App::PropertyBool",
@@ -170,25 +151,20 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
(
"Computation of incompressible material in connection\n"
"with viscoelastic Maxwell material and a custom 'Variable'"
)
),
)
obj.addProperty(
"App::PropertyBool",
"MaxwellMaterial",
"Elasticity",
"Compute viscoelastic material model"
)
obj.addProperty(
"App::PropertyBool",
"ModelLumping",
"Elasticity",
"Use model lumping"
"Compute viscoelastic material model",
)
obj.addProperty("App::PropertyBool", "ModelLumping", "Elasticity", "Use model lumping")
obj.addProperty(
"App::PropertyFile",
"ModelLumpingFilename",
"Elasticity",
"File to save results from model lumping to"
"File to save results from model lumping to",
)
obj.addProperty(
"App::PropertyBool",
@@ -197,13 +173,10 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
(
"If true, 'Eigen Analysis' is stability analysis.\n"
"Otherwise modal analysis is performed."
)
),
)
obj.addProperty(
"App::PropertyBool",
"UpdateTransientSystem",
"Elasticity",
"See Elmer manual for info"
"App::PropertyBool", "UpdateTransientSystem", "Elasticity", "See Elmer manual for info"
)
obj.addProperty(
"App::PropertyString",
@@ -212,7 +185,7 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
(
"Only change this if 'Incompressible' is set to true\n"
"according to the Elmer manual."
)
),
)
obj.addProperty(
@@ -222,7 +195,7 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
(
"Computes solution according to plane\nstress situation.\n"
"Applies only for 2D geometry."
)
),
)
obj.EigenSystemValues = 5
@@ -243,8 +216,8 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
obj.EigenSystemSelect = "Smallest Magnitude"
# according to Elmer manual default is 100 times the Linear Tolerance
# since this is a small value we must set an expression, see linear.py for background
for (prop, expr) in obj.ExpressionEngine:
if (prop == "LinearTolerance"):
for prop, expr in obj.ExpressionEngine:
if prop == "LinearTolerance":
obj.setExpression("EigenSystemTolerance", str(100 * obj.evalExpression(expr)))
obj.Variable = "Displacement"
@@ -252,4 +225,5 @@ class Proxy(linear.Proxy, equationbase.ElasticityProxy):
class ViewProxy(linear.ViewProxy, equationbase.ElasticityViewProxy):
pass
## @}

View File

@@ -63,8 +63,7 @@ class ElasticityWriter:
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 Convergence Tolerance"] = equation.EigenSystemTolerance
s["Eigen System Complex"] = equation.EigenSystemComplex
if equation.EigenSystemComputeResiduals is True:
s["Eigen System Compute Residuals"] = equation.EigenSystemComputeResiduals
@@ -110,7 +109,7 @@ class ElasticityWriter:
(
"Only change this if 'Incompressible' is set to true\n"
"according to the Elmer manual."
)
),
)
equation.Variable = "Displacement"
if hasattr(equation, "Bubbles"):
@@ -118,10 +117,7 @@ class ElasticityWriter:
equation.removeProperty("Bubbles")
if not hasattr(equation, "ConstantBulkSystem"):
equation.addProperty(
"App::PropertyBool",
"ConstantBulkSystem",
"Elasticity",
"See Elmer manual for info"
"App::PropertyBool", "ConstantBulkSystem", "Elasticity", "See Elmer manual for info"
)
if not hasattr(equation, "DisplaceMesh"):
equation.addProperty(
@@ -131,7 +127,7 @@ class ElasticityWriter:
(
"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
@@ -142,10 +138,7 @@ class ElasticityWriter:
# DoFrequencyAnalysis was renamed to EigenAnalysis
# to follow the Elmer manual
equation.addProperty(
"App::PropertyBool",
"EigenAnalysis",
"Eigen Values",
"If true, modal analysis"
"App::PropertyBool", "EigenAnalysis", "Eigen Values", "If true, modal analysis"
)
if hasattr(equation, "DoFrequencyAnalysis"):
equation.EigenAnalysis = equation.DoFrequencyAnalysis
@@ -158,7 +151,7 @@ class ElasticityWriter:
(
"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"):
@@ -166,7 +159,7 @@ class ElasticityWriter:
"App::PropertyBool",
"EigenSystemComputeResiduals",
"Eigen Values",
"Computes residuals of eigen value system"
"Computes residuals of eigen value system",
)
if not hasattr(equation, "EigenSystemDamped"):
equation.addProperty(
@@ -176,14 +169,14 @@ class ElasticityWriter:
(
"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"
"Max iterations for iterative eigensystem solver",
)
equation.EigenSystemMaxIterations = (300, 1, int(1e8), 1)
if not hasattr(equation, "EigenSystemSelect"):
@@ -191,7 +184,7 @@ class ElasticityWriter:
"App::PropertyEnumeration",
"EigenSystemSelect",
"Eigen Values",
"Which eigenvalues are computed"
"Which eigenvalues are computed",
)
equation.EigenSystemSelect = elasticity.EIGEN_SYSTEM_SELECT
equation.EigenSystemSelect = "Smallest Magnitude"
@@ -203,7 +196,7 @@ class ElasticityWriter:
(
"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"):
@@ -213,7 +206,7 @@ class ElasticityWriter:
"App::PropertyInteger",
"EigenSystemValues",
"Eigen Values",
"Number of lowest eigen modes"
"Number of lowest eigen modes",
)
if hasattr(equation, "EigenmodesCount"):
equation.EigenSystemValues = equation.EigenmodesCount
@@ -223,14 +216,14 @@ class ElasticityWriter:
"App::PropertyBool",
"FixDisplacement",
"Elasticity",
"If displacements or forces are set,\nthereby model lumping is used"
"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"
"Consider geometric stiffness",
)
if not hasattr(equation, "Incompressible"):
equation.addProperty(
@@ -240,28 +233,25 @@ class ElasticityWriter:
(
"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"
"Compute viscoelastic material model",
)
if not hasattr(equation, "ModelLumping"):
equation.addProperty(
"App::PropertyBool",
"ModelLumping",
"Elasticity",
"Use model lumping"
"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"
"File to save results from model lumping to",
)
if not hasattr(equation, "PlaneStress"):
equation.addProperty(
@@ -271,7 +261,7 @@ class ElasticityWriter:
(
"Computes solution according to plane\nstress situation.\n"
"Applies only for 2D geometry."
)
),
)
if not hasattr(equation, "StabilityAnalysis"):
equation.addProperty(
@@ -281,14 +271,14 @@ class ElasticityWriter:
(
"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"
"See Elmer manual for info",
)
def handleElasticityConstants(self):
@@ -367,7 +357,7 @@ class ElasticityWriter:
gravity = self.write.convert(obj.GravityAcceleration.toStr(), "L/T^2")
if self.write.getBodyMaterial(name) is None:
raise general_writer.WriteError(
"The body {} is not referenced in any material.\n\n".format(name)
f"The body {name} is not referenced in any material.\n\n"
)
m = self.write.getBodyMaterial(name).Material
@@ -409,17 +399,13 @@ class ElasticityWriter:
# get the material data for all bodies
for obj in self.write.getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self.write.getAllBodies()
)
refs = obj.References[0][1] if obj.References else self.write.getAllBodies()
for name in (n for n in refs if n in bodies):
# don't evaluate fluid material
if self.write.isBodyMaterialFluid(name):
break
if "YoungsModulus" not in m:
Console.PrintMessage("m: {}\n".format(m))
Console.PrintMessage(f"m: {m}\n")
# it is no fluid but also no solid
# -> user set no material reference at all
# that now material is known
@@ -429,22 +415,14 @@ class ElasticityWriter:
)
self.write.material(name, "Name", m["Name"])
if density_needed is True:
self.write.material(
name, "Density",
self.write.getDensity(m)
)
self.write.material(
name, "Youngs Modulus",
self._getYoungsModulus(m)
)
self.write.material(
name, "Poisson ratio",
float(m["PoissonRatio"])
)
self.write.material(name, "Density", self.write.getDensity(m))
self.write.material(name, "Youngs Modulus", self._getYoungsModulus(m))
self.write.material(name, "Poisson ratio", float(m["PoissonRatio"]))
if tempObj:
self.write.material(
name, "Heat expansion Coefficient",
self.write.convert(m["ThermalExpansionCoefficient"], "O^-1")
name,
"Heat expansion Coefficient",
self.write.convert(m["ThermalExpansionCoefficient"], "O^-1"),
)
def _getYoungsModulus(self, m):
@@ -453,4 +431,5 @@ class ElasticityWriter:
youngsModulus *= 1e3
return youngsModulus
## @}

View File

@@ -36,8 +36,7 @@ SOLVER_EXEC_METHODS = ["After Timestep", "Always"]
def create(doc, name="Electricforce"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(linear.Proxy, equationbase.ElectricforceProxy):
@@ -45,7 +44,7 @@ class Proxy(linear.Proxy, equationbase.ElectricforceProxy):
Type = "Fem::EquationElmerElectricforce"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyEnumeration",
@@ -54,7 +53,7 @@ class Proxy(linear.Proxy, equationbase.ElectricforceProxy):
(
"That solver is only executed after solution converged\n"
"To execute always, change to 'Always'"
)
),
)
obj.ExecSolver = SOLVER_EXEC_METHODS
@@ -68,4 +67,5 @@ class Proxy(linear.Proxy, equationbase.ElectricforceProxy):
class ViewProxy(linear.ViewProxy, equationbase.ElectricforceViewProxy):
pass
## @}

View File

@@ -60,9 +60,10 @@ class EFwriter:
(
"That solver is only executed after solution converged\n"
"To execute always, change to 'Always'"
)
),
)
equation.ExecSolver = electricforce.SOLVER_EXEC_METHODS
equation.ExecSolver = "After Timestep"
## @}

View File

@@ -35,8 +35,7 @@ from . import linear
def create(doc, name="Electrostatic"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(linear.Proxy, equationbase.ElectrostaticProxy):
@@ -44,38 +43,13 @@ class Proxy(linear.Proxy, equationbase.ElectrostaticProxy):
Type = "Fem::EquationElmerElectrostatic"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyBool",
"CalculateCapacitanceMatrix",
"Electrostatic",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateElectricEnergy",
"Electrostatic",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateElectricField",
"Electrostatic",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateElectricFlux",
"Electrostatic",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateSurfaceCharge",
"Electrostatic",
""
)
obj.addProperty("App::PropertyBool", "CalculateCapacitanceMatrix", "Electrostatic", "")
obj.addProperty("App::PropertyBool", "CalculateElectricEnergy", "Electrostatic", "")
obj.addProperty("App::PropertyBool", "CalculateElectricField", "Electrostatic", "")
obj.addProperty("App::PropertyBool", "CalculateElectricFlux", "Electrostatic", "")
obj.addProperty("App::PropertyBool", "CalculateSurfaceCharge", "Electrostatic", "")
"""
obj.addProperty(
"App::PropertyInteger",
@@ -91,13 +65,13 @@ class Proxy(linear.Proxy, equationbase.ElectrostaticProxy):
(
"File where capacitance matrix is being saved\n"
"Only used if 'CalculateCapacitanceMatrix' is true"
)
),
)
obj.addProperty(
"App::PropertyBool",
"ConstantWeights",
"Electrostatic",
"Use constant weighting for results"
"Use constant weighting for results",
)
obj.addProperty(
"App::PropertyFloat",
@@ -106,7 +80,7 @@ class Proxy(linear.Proxy, equationbase.ElectrostaticProxy):
(
"Potential difference in Volt for which capacitance is\n"
"calculated if 'CalculateCapacitanceMatrix' is false"
)
),
)
obj.CapacitanceMatrixFilename = "cmatrix.dat"
@@ -117,4 +91,5 @@ class Proxy(linear.Proxy, equationbase.ElectrostaticProxy):
class ViewProxy(linear.ViewProxy, equationbase.ElectrostaticViewProxy):
pass
## @}

View File

@@ -63,10 +63,7 @@ class ESwriter:
s["Constant Weights"] = equation.ConstantWeights
s["Exec Solver"] = "Always"
s["Optimize Bandwidth"] = True
if (
equation.CalculateCapacitanceMatrix is False
and (equation.PotentialDifference != 0.0)
):
if equation.CalculateCapacitanceMatrix is False and (equation.PotentialDifference != 0.0):
s["Potential Difference"] = equation.PotentialDifference
s["Stabilize"] = equation.Stabilize
return s
@@ -81,7 +78,7 @@ class ESwriter:
(
"File where capacitance matrix is being saved\n"
"Only used if 'CalculateCapacitanceMatrix' is true"
)
),
)
equation.CapacitanceMatrixFilename = "cmatrix.dat"
if not hasattr(equation, "ConstantWeights"):
@@ -89,7 +86,7 @@ class ESwriter:
"App::PropertyBool",
"ConstantWeights",
"Electrostatic",
"Use constant weighting for results"
"Use constant weighting for results",
)
if not hasattr(equation, "PotentialDifference"):
equation.addProperty(
@@ -99,14 +96,13 @@ class ESwriter:
(
"Potential difference in Volt for which capacitance is\n"
"calculated if 'CalculateCapacitanceMatrix' is false"
)
),
)
equation.PotentialDifference = 0.0
def handleElectrostaticConstants(self):
permittivity = self.write.convert(
self.write.constsdef["PermittivityOfVacuum"],
"T^4*I^2/(L^3*M)"
self.write.constsdef["PermittivityOfVacuum"], "T^4*I^2/(L^3*M)"
)
permittivity = round(permittivity, 20) # to get rid of numerical artifacts
self.write.constant("Permittivity Of Vacuum", permittivity)
@@ -114,16 +110,12 @@ class ESwriter:
def handleElectrostaticMaterial(self, bodies):
for obj in self.write.getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self.write.getAllBodies())
refs = obj.References[0][1] if obj.References else self.write.getAllBodies()
for name in (n for n in refs if n in bodies):
self.write.material(name, "Name", m["Name"])
if "RelativePermittivity" in m:
self.write.material(
name, "Relative Permittivity",
float(m["RelativePermittivity"])
name, "Relative Permittivity", float(m["RelativePermittivity"])
)
def handleElectrostaticBndConditions(self):
@@ -143,7 +135,7 @@ class ESwriter:
"App::PropertyElectricPotential",
"Potential",
"Parameter",
"Electric Potential"
"Electric Potential",
)
# scale to match SI units
obj.Potential = savePotential * 1e6
@@ -160,4 +152,5 @@ class ESwriter:
self.write.boundary(name, "Capacitance Body", obj.CapacitanceBody)
self.write.handled(obj)
## @}

View File

@@ -40,7 +40,7 @@ if App.GuiUp:
class Proxy(equationbase.BaseProxy):
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyInteger",
"Priority",
@@ -49,7 +49,7 @@ class Proxy(equationbase.BaseProxy):
"Number of your choice\n"
"The equation with highest number\n"
"will be solved first."
)
),
)
@@ -72,14 +72,13 @@ class ViewProxy(equationbase.BaseViewProxy):
return None
class _TaskPanel(object):
class _TaskPanel:
def __init__(self, obj):
self._obj = obj
self._refWidget = selection_widgets.SolidSelector()
self._refWidget.setReferences(obj.References)
propWidget = obj.ViewObject.Proxy.getTaskWidget(
obj.ViewObject)
propWidget = obj.ViewObject.Proxy.getTaskWidget(obj.ViewObject)
if propWidget is None:
self.form = self._refWidget
else:

View File

@@ -38,8 +38,7 @@ FLOW_MODEL = ["Full", "No convection", "Stokes"]
def create(doc, name="Flow"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(nonlinear.Proxy, equationbase.FlowProxy):
@@ -47,7 +46,7 @@ class Proxy(nonlinear.Proxy, equationbase.FlowProxy):
Type = "Fem::EquationElmerFlow"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyBool",
@@ -56,14 +55,9 @@ class Proxy(nonlinear.Proxy, equationbase.FlowProxy):
(
"Set to true for incompressible flow for more stable\n"
"discretization when Reynolds number increases"
)
)
obj.addProperty(
"App::PropertyEnumeration",
"FlowModel",
"Flow",
"Flow model to be used"
),
)
obj.addProperty("App::PropertyEnumeration", "FlowModel", "Flow", "Flow model to be used")
obj.addProperty(
"App::PropertyBool",
"GradpDiscretization",
@@ -71,20 +65,14 @@ class Proxy(nonlinear.Proxy, equationbase.FlowProxy):
(
"If true pressure Dirichlet boundary conditions can be used.\n"
"Also mass flux is available as a natural boundary condition."
)
),
)
obj.addProperty(
"App::PropertyString",
"Variable",
"Flow",
"Only for a 2D model change the '3' to '2'"
"App::PropertyString", "Variable", "Flow", "Only for a 2D model change the '3' to '2'"
)
obj.addProperty(
"App::PropertyEnumeration",
"Convection",
"Equation",
"Type of convection to be used"
"App::PropertyEnumeration", "Convection", "Equation", "Type of convection to be used"
)
obj.addProperty(
"App::PropertyBool",
@@ -93,7 +81,7 @@ class Proxy(nonlinear.Proxy, equationbase.FlowProxy):
(
"Magnetic induction equation will be solved\n"
"along with the Navier-Stokes equations"
)
),
)
obj.FlowModel = FLOW_MODEL
obj.FlowModel = "Full"
@@ -106,4 +94,5 @@ class Proxy(nonlinear.Proxy, equationbase.FlowProxy):
class ViewProxy(nonlinear.ViewProxy, equationbase.FlowViewProxy):
pass
## @}

View File

@@ -72,7 +72,7 @@ class Flowwriter:
"App::PropertyEnumeration",
"Convection",
"Equation",
"Type of convection to be used"
"Type of convection to be used",
)
equation.Convection = flow.CONVECTION_TYPE
equation.Convection = "Computed"
@@ -84,14 +84,11 @@ class Flowwriter:
(
"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"
"App::PropertyEnumeration", "FlowModel", "Flow", "Flow model to be used"
)
equation.FlowModel = flow.FLOW_MODEL
equation.FlowModel = "Full"
@@ -103,7 +100,7 @@ class Flowwriter:
(
"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(
@@ -113,14 +110,14 @@ class Flowwriter:
(
"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'"
"Only for a 2D model change the '3' to '2'",
)
equation.Variable = "Flow Solution[Velocity:3 Pressure:1]"
@@ -132,10 +129,7 @@ class Flowwriter:
self.write.material(name, "Reference Temperature", refTemp)
for obj in self.write.getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self.write.getAllBodies())
refs = obj.References[0][1] if obj.References else self.write.getAllBodies()
for name in (n for n in refs if n in bodies):
self.write.material(name, "Name", m["Name"])
if "Density" in m:
@@ -161,9 +155,7 @@ class Flowwriter:
if "SpecificHeatRatio" in m:
self.write.material(name, "Specific Heat Ratio", float(m["SpecificHeatRatio"]))
if "CompressibilityModel" in m:
self.write.material(
name, "Compressibility Model",
m["CompressibilityModel"])
self.write.material(name, "Compressibility Model", m["CompressibilityModel"])
def _outputInitialPressure(self, obj, name):
# initial pressure only makes sense for fluid material
@@ -276,4 +268,5 @@ class Flowwriter:
if equation.MagneticInduction is True:
self.write.equation(b, "Magnetic Induction", equation.MagneticInduction)
## @}

View File

@@ -39,8 +39,7 @@ VARIABLES = ["Potential", "Temperature"]
def create(doc, name="Flux"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(linear.Proxy, equationbase.FluxProxy):
@@ -48,7 +47,7 @@ class Proxy(linear.Proxy, equationbase.FluxProxy):
Type = "Fem::EquationElmerFlux"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyBool",
@@ -57,43 +56,29 @@ class Proxy(linear.Proxy, equationbase.FluxProxy):
(
"Enforces continuity within the same material\n"
"in the 'Discontinuous Galerkin' discretization"
)
),
)
obj.addProperty("App::PropertyBool", "CalculateFlux", "Flux", "Computes flux vector")
obj.addProperty(
"App::PropertyBool",
"CalculateFlux",
"Flux",
"Computes flux vector"
)
obj.addProperty(
"App::PropertyBool",
"CalculateFluxAbs",
"Flux",
"Computes absolute of flux vector"
"App::PropertyBool", "CalculateFluxAbs", "Flux", "Computes absolute of flux vector"
)
obj.addProperty(
"App::PropertyBool",
"CalculateFluxMagnitude",
"Flux",
"Computes magnitude of flux vector field"
"Computes magnitude of flux vector field",
)
obj.addProperty(
"App::PropertyBool",
"CalculateGrad",
"Flux",
"Select calculation of gradient"
"App::PropertyBool", "CalculateGrad", "Flux", "Select calculation of gradient"
)
obj.addProperty(
"App::PropertyBool",
"CalculateGradAbs",
"Flux",
"Computes absolute of gradient field"
"App::PropertyBool", "CalculateGradAbs", "Flux", "Computes absolute of gradient field"
)
obj.addProperty(
"App::PropertyBool",
"CalculateGradMagnitude",
"Flux",
"Computes magnitude of gradient field"
"Computes magnitude of gradient field",
)
obj.addProperty(
"App::PropertyBool",
@@ -102,7 +87,7 @@ class Proxy(linear.Proxy, equationbase.FluxProxy):
(
"Enable if standard Galerkin approximation leads to\n"
"unphysical results when there are discontinuities"
)
),
)
obj.addProperty(
"App::PropertyBool",
@@ -111,19 +96,16 @@ class Proxy(linear.Proxy, equationbase.FluxProxy):
(
"If true, negative values of computed magnitude fields\n"
"are a posteriori set to zero."
)
),
)
obj.addProperty(
"App::PropertyEnumeration",
"FluxCoefficient",
"Flux",
"Proportionality coefficient\nto compute the flux"
"Proportionality coefficient\nto compute the flux",
)
obj.addProperty(
"App::PropertyEnumeration",
"FluxVariable",
"Flux",
"Variable name for flux calculation"
"App::PropertyEnumeration", "FluxVariable", "Flux", "Variable name for flux calculation"
)
obj.CalculateFlux = True
@@ -141,4 +123,5 @@ class Proxy(linear.Proxy, equationbase.FluxProxy):
class ViewProxy(linear.ViewProxy, equationbase.FluxViewProxy):
pass
## @}

View File

@@ -78,38 +78,35 @@ class Fluxwriter:
(
"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"
"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"
"Computes magnitude of flux vector field",
)
if not hasattr(equation, "CalculateGradAbs"):
equation.addProperty(
"App::PropertyBool",
"CalculateGradAbs",
"Flux",
"Computes absolute of gradient field"
"Computes absolute of gradient field",
)
if not hasattr(equation, "CalculateGradMagnitude"):
equation.addProperty(
"App::PropertyBool",
"CalculateGradMagnitude",
"Flux",
"Computes magnitude of gradient field"
"Computes magnitude of gradient field",
)
if not hasattr(equation, "DiscontinuousGalerkin"):
equation.addProperty(
@@ -119,7 +116,7 @@ class Fluxwriter:
(
"Enable if standard Galerkin approximation leads to\n"
"unphysical results when there are discontinuities"
)
),
)
if not hasattr(equation, "EnforcePositiveMagnitude"):
equation.addProperty(
@@ -129,7 +126,7 @@ class Fluxwriter:
(
"If true, negative values of computed magnitude fields\n"
"are a posteriori set to zero."
)
),
)
tempFluxCoefficient = ""
if hasattr(equation, "FluxCoefficient"):
@@ -143,7 +140,7 @@ class Fluxwriter:
"App::PropertyEnumeration",
"FluxCoefficient",
"Flux",
"Name of proportionality coefficient\nto compute the flux"
"Name of proportionality coefficient\nto compute the flux",
)
equation.FluxCoefficient = flux.COEFFICIENTS
if tempFluxCoefficient:
@@ -161,9 +158,10 @@ class Fluxwriter:
"App::PropertyEnumeration",
"FluxVariable",
"Flux",
"Variable name for flux calculation"
"Variable name for flux calculation",
)
equation.FluxVariable = flux.VARIABLES
equation.FluxVariable = tempFluxVariable
## @}

View File

@@ -38,8 +38,7 @@ PHASE_CHANGE_MODEL = ["None", "Spatial 1", "Spatial 2", "Temporal"]
def create(doc, name="Heat"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(nonlinear.Proxy, equationbase.HeatProxy):
@@ -47,27 +46,16 @@ class Proxy(nonlinear.Proxy, equationbase.HeatProxy):
Type = "Fem::EquationElmerHeat"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
# according to the Elmer models manual Bubbles is by default True
# and Stabilize is False (Stabilize is added in linear.py)
obj.addProperty("App::PropertyBool", "Bubbles", "Heat", "")
obj.addProperty(
"App::PropertyBool",
"Bubbles",
"Heat",
""
"App::PropertyEnumeration", "Convection", "Equation", "Type of convection to be used"
)
obj.addProperty(
"App::PropertyEnumeration",
"Convection",
"Equation",
"Type of convection to be used"
)
obj.addProperty(
"App::PropertyEnumeration",
"PhaseChangeModel",
"Equation",
"Model for phase change"
"App::PropertyEnumeration", "PhaseChangeModel", "Equation", "Model for phase change"
)
obj.Bubbles = True
@@ -82,4 +70,5 @@ class Proxy(nonlinear.Proxy, equationbase.HeatProxy):
class ViewProxy(nonlinear.ViewProxy, equationbase.HeatViewProxy):
pass
## @}

View File

@@ -63,7 +63,7 @@ class Heatwriter:
def handleHeatConstants(self):
self.write.constant(
"Stefan Boltzmann",
self.write.convert(self.write.constsdef["StefanBoltzmann"], "M/(O^4*T^3)")
self.write.convert(self.write.constsdef["StefanBoltzmann"], "M/(O^4*T^3)"),
)
def handleHeatEquation(self, bodies, equation):
@@ -80,16 +80,13 @@ class Heatwriter:
"App::PropertyEnumeration",
"Convection",
"Equation",
"Type of convection to be used"
"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"
"App::PropertyEnumeration", "PhaseChangeModel", "Equation", "Model for phase change"
)
equation.PhaseChangeModel = heat.PHASE_CHANGE_MODEL
equation.PhaseChangeModel = "None"
@@ -99,12 +96,10 @@ class Heatwriter:
for obj in self.write.getMember("Fem::ConstraintTemperature"):
i = i + 1
femobjects = membertools.get_several_member(
self.write.analysis,
"Fem::ConstraintTemperature"
self.write.analysis, "Fem::ConstraintTemperature"
)
femobjects[i]["Nodes"] = meshtools.get_femnodes_by_femobj_with_references(
self.write.getSingleMember("Fem::FemMeshObject").FemMesh,
femobjects[i]
self.write.getSingleMember("Fem::FemMeshObject").FemMesh, femobjects[i]
)
NumberOfNodes = len(femobjects[i]["Nodes"])
if obj.References:
@@ -150,7 +145,9 @@ class Heatwriter:
ref_sub_obj = ref[1][0]
density = None
for mat in self.write.getMember("App::MaterialObject"):
mat_ref = [*itertools.chain(*[itertools.product([i[0]],i[1]) for i in mat.References])]
mat_ref = [
*itertools.chain(*[itertools.product([i[0]], i[1]) for i in mat.References])
]
if (ref_feat, ref_sub_obj) in mat_ref:
density = FreeCAD.Units.Quantity(mat.Material["Density"])
break
@@ -162,8 +159,12 @@ class Heatwriter:
density = FreeCAD.Units.Quantity(mat.Material["Density"])
break
volume = ref_feat.getSubObject(ref_sub_obj).Volume
heatSource = (obj.TotalPower / (density*FreeCAD.Units.Quantity(volume, "mm^3"))).getValueAs("W/kg").Value
heatSource = (
(obj.TotalPower / (density * FreeCAD.Units.Quantity(volume, "mm^3")))
.getValueAs("W/kg")
.Value
)
if heatSource == 0.0:
# a zero heat would break Elmer (division by zero)
raise general_writer.WriteError("The body heat source must not be zero!")
@@ -197,32 +198,30 @@ class Heatwriter:
self.write.material(name, "Reference Temperature", refTemp)
for obj in self.write.getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self.write.getAllBodies())
refs = obj.References[0][1] if obj.References else self.write.getAllBodies()
for name in (n for n in refs if n in bodies):
if "Density" not in m:
raise general_writer.WriteError(
"Used material does not specify the necessary 'Density'."
)
self.write.material(name, "Name", m["Name"])
self.write.material(
name, "Density",
self.write.getDensity(m))
self.write.material(name, "Density", self.write.getDensity(m))
if "ThermalConductivity" not in m:
raise general_writer.WriteError(
"Used material does not specify the necessary 'Thermal Conductivity'."
)
self.write.material(
name, "Heat Conductivity",
self.write.convert(m["ThermalConductivity"], "M*L/(T^3*O)"))
name,
"Heat Conductivity",
self.write.convert(m["ThermalConductivity"], "M*L/(T^3*O)"),
)
if "SpecificHeat" not in m:
raise general_writer.WriteError(
"Used material does not specify the necessary 'Specific Heat'."
)
self.write.material(
name, "Heat Capacity",
self.write.convert(m["SpecificHeat"], "L^2/(T^2*O)"))
name, "Heat Capacity", self.write.convert(m["SpecificHeat"], "L^2/(T^2*O)")
)
## @}

View File

@@ -39,75 +39,32 @@ from . import equation
LINEAR_SOLVER = ["Direct", "Iterative"]
LINEAR_DIRECT = ["Banded", "MUMPS", "Umfpack"]
LINEAR_ITERATIVE = [
"BiCGStab",
"BiCGStabl",
"CG",
"GCR",
"CGS",
"GMRES",
"Idrs",
"TFQMR"
]
LINEAR_PRECONDITIONING = [
"None",
"Diagonal",
"ILU0",
"ILU1",
"ILU2",
"ILU3",
"ILU4",
"ILUT"
]
LINEAR_ITERATIVE = ["BiCGStab", "BiCGStabl", "CG", "GCR", "CGS", "GMRES", "Idrs", "TFQMR"]
LINEAR_PRECONDITIONING = ["None", "Diagonal", "ILU0", "ILU1", "ILU2", "ILU3", "ILU4", "ILUT"]
class Proxy(equation.Proxy):
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyIntegerConstraint",
"BiCGstablDegree",
"Linear System",
"Polynom degree for iterative method 'BiCGstabl'"
"Polynom degree for iterative method 'BiCGstabl'",
)
obj.addProperty(
"App::PropertyIntegerConstraint",
"IdrsParameter",
"Linear System",
"Parameter for iterative method 'Idrs'"
)
obj.addProperty(
"App::PropertyEnumeration",
"LinearDirectMethod",
"Linear System",
""
)
obj.addProperty(
"App::PropertyIntegerConstraint",
"LinearIterations",
"Linear System",
""
)
obj.addProperty(
"App::PropertyEnumeration",
"LinearIterativeMethod",
"Linear System",
""
)
obj.addProperty(
"App::PropertyEnumeration",
"LinearPreconditioning",
"Linear System",
""
)
obj.addProperty(
"App::PropertyEnumeration",
"LinearSolverType",
"Linear System",
""
"Parameter for iterative method 'Idrs'",
)
obj.addProperty("App::PropertyEnumeration", "LinearDirectMethod", "Linear System", "")
obj.addProperty("App::PropertyIntegerConstraint", "LinearIterations", "Linear System", "")
obj.addProperty("App::PropertyEnumeration", "LinearIterativeMethod", "Linear System", "")
obj.addProperty("App::PropertyEnumeration", "LinearPreconditioning", "Linear System", "")
obj.addProperty("App::PropertyEnumeration", "LinearSolverType", "Linear System", "")
obj.addProperty(
"App::PropertyBool",
"LinearSystemSolverDisabled",
@@ -116,26 +73,16 @@ class Proxy(equation.Proxy):
"Disable the linear system.\n"
"Only use for special cases\n"
"and consult the Elmer docs."
)
),
)
obj.addProperty(
"App::PropertyFloat",
"LinearTolerance",
"Linear System",
"Linear preconditioning method"
)
obj.addProperty(
"App::PropertyBool",
"Stabilize",
"Base",
""
)
obj.addProperty(
"App::PropertyFloat",
"SteadyStateTolerance",
"Steady State",
""
"Linear preconditioning method",
)
obj.addProperty("App::PropertyBool", "Stabilize", "Base", "")
obj.addProperty("App::PropertyFloat", "SteadyStateTolerance", "Steady State", "")
obj.BiCGstablDegree = (2, 2, 10, 1)
obj.IdrsParameter = (2, 1, 10, 1)
@@ -160,4 +107,5 @@ class Proxy(equation.Proxy):
class ViewProxy(equation.ViewProxy):
pass
## @}

View File

@@ -34,8 +34,7 @@ from ... import equationbase
def create(doc, name="Magnetodynamic"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(nonlinear.Proxy, equationbase.MagnetodynamicProxy):
@@ -43,19 +42,19 @@ class Proxy(nonlinear.Proxy, equationbase.MagnetodynamicProxy):
Type = "Fem::EquationElmerMagnetodynamic"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyBool",
"IsHarmonic",
"Magnetodynamic",
"If the magnetic source is harmonically driven"
"If the magnetic source is harmonically driven",
)
obj.addProperty(
"App::PropertyFrequency",
"AngularFrequency",
"Magnetodynamic",
"Frequency of the driving current"
"Frequency of the driving current",
)
obj.addProperty(
"App::PropertyBool",
@@ -63,129 +62,68 @@ class Proxy(nonlinear.Proxy, equationbase.MagnetodynamicProxy):
"Magnetodynamic",
"Must be True if basis functions for edge element interpolation\n"
"are selected to be members of optimal edge element family\n"
"or if second-order approximation is used."
"or if second-order approximation is used.",
)
obj.addProperty(
"App::PropertyBool",
"QuadraticApproximation",
"Magnetodynamic",
"Enables second-order approximation of driving current"
"Enables second-order approximation of driving current",
)
obj.addProperty(
"App::PropertyBool",
"StaticConductivity",
"Magnetodynamic",
"See Elmer models manual for info"
"See Elmer models manual for info",
)
obj.addProperty(
"App::PropertyBool",
"FixInputCurrentDensity",
"Magnetodynamic",
"Ensures divergence-freeness of current density"
"Ensures divergence-freeness of current density",
)
obj.addProperty(
"App::PropertyBool",
"AutomatedSourceProjectionBCs",
"Magnetodynamic",
"See Elmer models manual for info"
"See Elmer models manual for info",
)
obj.addProperty(
"App::PropertyBool",
"UseLagrangeGauge",
"Magnetodynamic",
"See Elmer models manual for info"
"See Elmer models manual for info",
)
obj.addProperty(
"App::PropertyFloat",
"LagrangeGaugePenalizationCoefficient",
"Magnetodynamic",
"See Elmer models manual for info"
"See Elmer models manual for info",
)
obj.addProperty(
"App::PropertyBool",
"UseTreeGauge",
"Magnetodynamic",
"See Elmer models manual for info\n"
"Will be ignored if 'UsePiolaTransform' is True"
)
obj.addProperty(
"App::PropertyBool",
"LinearSystemRefactorize",
"Linear System",
""
"See Elmer models manual for info\nWill be ignored if 'UsePiolaTransform' is True",
)
obj.addProperty("App::PropertyBool", "LinearSystemRefactorize", "Linear System", "")
obj.IsHarmonic = False
obj.AngularFrequency = 0
obj.Priority = 10
# the post processor options
obj.addProperty(
"App::PropertyBool",
"CalculateCurrentDensity",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateElectricField",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateElementalFields",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateHarmonicLoss",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateJouleHeating",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateMagneticFieldStrength",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateMaxwellStress",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateNodalFields",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateNodalForces",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateNodalHeating",
"Results",
""
)
obj.addProperty(
"App::PropertyBool",
"DiscontinuousBodies",
"Results",
""
)
obj.addProperty("App::PropertyBool", "CalculateCurrentDensity", "Results", "")
obj.addProperty("App::PropertyBool", "CalculateElectricField", "Results", "")
obj.addProperty("App::PropertyBool", "CalculateElementalFields", "Results", "")
obj.addProperty("App::PropertyBool", "CalculateHarmonicLoss", "Results", "")
obj.addProperty("App::PropertyBool", "CalculateJouleHeating", "Results", "")
obj.addProperty("App::PropertyBool", "CalculateMagneticFieldStrength", "Results", "")
obj.addProperty("App::PropertyBool", "CalculateMaxwellStress", "Results", "")
obj.addProperty("App::PropertyBool", "CalculateNodalFields", "Results", "")
obj.addProperty("App::PropertyBool", "CalculateNodalForces", "Results", "")
obj.addProperty("App::PropertyBool", "CalculateNodalHeating", "Results", "")
obj.addProperty("App::PropertyBool", "DiscontinuousBodies", "Results", "")
obj.CalculateCurrentDensity = False
obj.CalculateElectricField = False
# FIXME: at the moment FreeCAD's post processor cannot display elementary field
@@ -204,4 +142,5 @@ class Proxy(nonlinear.Proxy, equationbase.MagnetodynamicProxy):
class ViewProxy(nonlinear.ViewProxy, equationbase.MagnetodynamicViewProxy):
pass
## @}

View File

@@ -34,8 +34,7 @@ from ... import equationbase
def create(doc, name="Magnetodynamic2D"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(nonlinear.Proxy, equationbase.Magnetodynamic2DProxy):
@@ -43,85 +42,37 @@ class Proxy(nonlinear.Proxy, equationbase.Magnetodynamic2DProxy):
Type = "Fem::EquationElmerMagnetodynamic2D"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyBool",
"IsHarmonic",
"Magnetodynamic2D",
"If the magnetic source is harmonically driven"
"If the magnetic source is harmonically driven",
)
obj.addProperty(
"App::PropertyFrequency",
"AngularFrequency",
"Magnetodynamic2D",
"Frequency of the driving current"
"Frequency of the driving current",
)
obj.IsHarmonic = False
obj.AngularFrequency = 0
obj.Priority = 10
# the post processor options
obj.addProperty("App::PropertyBool", "CalculateCurrentDensity", "Magnetodynamic2D", "")
obj.addProperty("App::PropertyBool", "CalculateElectricField", "Magnetodynamic2D", "")
obj.addProperty("App::PropertyBool", "CalculateElementalFields", "Magnetodynamic2D", "")
obj.addProperty("App::PropertyBool", "CalculateHarmonicLoss", "Magnetodynamic2D", "")
obj.addProperty("App::PropertyBool", "CalculateJouleHeating", "Magnetodynamic2D", "")
obj.addProperty(
"App::PropertyBool",
"CalculateCurrentDensity",
"Magnetodynamic2D",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateElectricField",
"Magnetodynamic2D",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateElementalFields",
"Magnetodynamic2D",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateHarmonicLoss",
"Magnetodynamic2D",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateJouleHeating",
"Magnetodynamic2D",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateMagneticFieldStrength",
"Magnetodynamic2D",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateMaxwellStress",
"Magnetodynamic2D",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateNodalFields",
"Magnetodynamic2D",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateNodalForces",
"Magnetodynamic2D",
""
)
obj.addProperty(
"App::PropertyBool",
"CalculateNodalHeating",
"Magnetodynamic2D",
""
"App::PropertyBool", "CalculateMagneticFieldStrength", "Magnetodynamic2D", ""
)
obj.addProperty("App::PropertyBool", "CalculateMaxwellStress", "Magnetodynamic2D", "")
obj.addProperty("App::PropertyBool", "CalculateNodalFields", "Magnetodynamic2D", "")
obj.addProperty("App::PropertyBool", "CalculateNodalForces", "Magnetodynamic2D", "")
obj.addProperty("App::PropertyBool", "CalculateNodalHeating", "Magnetodynamic2D", "")
obj.CalculateCurrentDensity = False
obj.CalculateElectricField = False
# FIXME: at the moment FreeCAD's post processor cannot display elementary field
@@ -139,4 +90,5 @@ class Proxy(nonlinear.Proxy, equationbase.Magnetodynamic2DProxy):
class ViewProxy(nonlinear.ViewProxy, equationbase.Magnetodynamic2DViewProxy):
pass
## @}

View File

@@ -92,15 +92,13 @@ class MgDyn2Dwriter:
def handleMagnetodynamic2DConstants(self):
permeability = self.write.convert(
self.write.constsdef["PermeabilityOfVacuum"],
"M*L/(T^2*I^2)"
self.write.constsdef["PermeabilityOfVacuum"], "M*L/(T^2*I^2)"
)
# we round in the following to get rid of numerical artifacts
self.write.constant("Permeability Of Vacuum", round(permeability, 20))
permittivity = self.write.convert(
self.write.constsdef["PermittivityOfVacuum"],
"T^4*I^2/(L^3*M)"
self.write.constsdef["PermittivityOfVacuum"], "T^4*I^2/(L^3*M)"
)
self.write.constant("Permittivity Of Vacuum", round(permittivity, 20))
@@ -109,22 +107,19 @@ class MgDyn2Dwriter:
for name in bodies:
if self.write.getBodyMaterial(name) is None:
raise general_writer.WriteError(
"The body {} is not referenced in any material.\n\n".format(name)
f"The body {name} is not referenced in any material.\n\n"
)
for obj in self.write.getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self.write.getAllBodies())
refs = obj.References[0][1] if obj.References else self.write.getAllBodies()
for name in (n for n in refs if n in bodies):
if "ElectricalConductivity" not in m:
Console.PrintMessage("m: {}\n".format(m))
Console.PrintMessage(f"m: {m}\n")
raise general_writer.WriteError(
"The electrical conductivity must be specified for all materials.\n\n"
)
if "RelativePermeability" not in m:
Console.PrintMessage("m: {}\n".format(m))
Console.PrintMessage(f"m: {m}\n")
raise general_writer.WriteError(
"The relative permeability must be specified for all materials.\n\n"
)
@@ -132,15 +127,11 @@ class MgDyn2Dwriter:
conductivity = self.write.convert(m["ElectricalConductivity"], "T^3*I^2/(L^3*M)")
conductivity = round(conductivity, 10) # to get rid of numerical artifacts
self.write.material(name, "Electric Conductivity", conductivity)
self.write.material(
name, "Relative Permeability",
float(m["RelativePermeability"])
)
self.write.material(name, "Relative Permeability", float(m["RelativePermeability"]))
# permittivity might be necessary for the post processor
if "RelativePermittivity" in m:
self.write.material(
name, "Relative Permittivity",
float(m["RelativePermittivity"])
name, "Relative Permittivity", float(m["RelativePermittivity"])
)
def _outputMagnetodynamic2DBodyForce(self, obj, name, equation):
@@ -229,12 +220,11 @@ class MgDyn2Dwriter:
def handleMagnetodynamic2DEquation(self, bodies, equation):
for b in bodies:
if equation.IsHarmonic and (equation.AngularFrequency == 0):
raise general_writer.WriteError(
"The angular frequency must not be zero.\n\n"
)
raise general_writer.WriteError("The angular frequency must not be zero.\n\n")
self.write.equation(b, "Name", equation.Name)
if equation.IsHarmonic:
frequency = float(Units.Quantity(equation.AngularFrequency).Value)
self.write.equation(b, "Angular Frequency", round(frequency, 6))
## @}

View File

@@ -73,8 +73,9 @@ class MgDynwriter:
if equation.UseLagrangeGauge is True:
s["Use Lagrange Gauge"] = True
if equation.LagrangeGaugePenalizationCoefficient != 0.0:
s["Lagrange Gauge Penalization Coefficient"] = \
s["Lagrange Gauge Penalization Coefficient"] = (
equation.LagrangeGaugePenalizationCoefficient
)
if equation.UseTreeGauge is True:
s["Use Tree Gauge"] = True
return s
@@ -117,15 +118,13 @@ class MgDynwriter:
def handleMagnetodynamicConstants(self):
permeability = self.write.convert(
self.write.constsdef["PermeabilityOfVacuum"],
"M*L/(T^2*I^2)"
self.write.constsdef["PermeabilityOfVacuum"], "M*L/(T^2*I^2)"
)
# we round in the following to get rid of numerical artifacts
self.write.constant("Permeability Of Vacuum", round(permeability, 20))
permittivity = self.write.convert(
self.write.constsdef["PermittivityOfVacuum"],
"T^4*I^2/(L^3*M)"
self.write.constsdef["PermittivityOfVacuum"], "T^4*I^2/(L^3*M)"
)
self.write.constant("Permittivity Of Vacuum", round(permittivity, 20))
@@ -134,22 +133,19 @@ class MgDynwriter:
for name in bodies:
if self.write.getBodyMaterial(name) is None:
raise general_writer.WriteError(
"The body {} is not referenced in any material.\n\n".format(name)
f"The body {name} is not referenced in any material.\n\n"
)
for obj in self.write.getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self.write.getAllBodies())
refs = obj.References[0][1] if obj.References else self.write.getAllBodies()
for name in (n for n in refs if n in bodies):
if "ElectricalConductivity" not in m:
Console.PrintMessage("m: {}\n".format(m))
Console.PrintMessage(f"m: {m}\n")
raise general_writer.WriteError(
"The electrical conductivity must be specified for all materials.\n\n"
)
if "RelativePermeability" not in m:
Console.PrintMessage("m: {}\n".format(m))
Console.PrintMessage(f"m: {m}\n")
raise general_writer.WriteError(
"The relative permeability must be specified for all materials.\n\n"
)
@@ -157,15 +153,11 @@ class MgDynwriter:
conductivity = self.write.convert(m["ElectricalConductivity"], "T^3*I^2/(L^3*M)")
conductivity = round(conductivity, 10) # to get rid of numerical artifacts
self.write.material(name, "Electric Conductivity", conductivity)
self.write.material(
name, "Relative Permeability",
float(m["RelativePermeability"])
)
self.write.material(name, "Relative Permeability", float(m["RelativePermeability"]))
# permittivity might be necessary for the post processor
if "RelativePermittivity" in m:
self.write.material(
name, "Relative Permittivity",
float(m["RelativePermittivity"])
name, "Relative Permittivity", float(m["RelativePermittivity"])
)
def _outputMagnetodynamicBodyForce(self, obj, name, equation):
@@ -235,7 +227,7 @@ class MgDynwriter:
for obj in currentDensities:
if obj.References:
firstName = obj.References[0][1][0]
firstName = firstName.rstrip('0123456789')
firstName = firstName.rstrip("0123456789")
if firstName == "Solid":
for name in obj.References[0][1]:
self._outputMagnetodynamicBodyForce(obj, name, equation)
@@ -278,7 +270,7 @@ class MgDynwriter:
for obj in potentials:
if obj.References:
firstName = obj.References[0][1][0]
firstName = firstName.rstrip('0123456789')
firstName = firstName.rstrip("0123456789")
if firstName == "Solid":
for name in obj.References[0][1]:
# output only if potentiual is enabled and needed
@@ -346,7 +338,7 @@ class MgDynwriter:
for obj in currentDensities:
if obj.References:
firstName = obj.References[0][1][0]
firstName = firstName.rstrip('0123456789')
firstName = firstName.rstrip("0123456789")
if firstName == "Face":
for name in obj.References[0][1]:
self._outputMagnetodynamicBndConditions(obj, name, equation)
@@ -363,7 +355,7 @@ class MgDynwriter:
for obj in potentials:
if obj.References:
firstName = obj.References[0][1][0]
firstName = firstName.rstrip('0123456789')
firstName = firstName.rstrip("0123456789")
if firstName == "Face":
for name in obj.References[0][1]:
# output the FreeCAD label as comment
@@ -373,4 +365,5 @@ class MgDynwriter:
self._outputMagnetodynamicBndConditions(obj, name, equation)
self.write.handled(obj)
## @}

View File

@@ -40,32 +40,24 @@ from . import linear
class Proxy(linear.Proxy):
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyIntegerConstraint",
"NonlinearIterations",
"Nonlinear System",
"Maximum number of iterations"
"Maximum number of iterations",
)
obj.addProperty(
"App::PropertyIntegerConstraint",
"NonlinearNewtonAfterIterations",
"Nonlinear System",
""
"",
)
obj.addProperty(
"App::PropertyFloat",
"NonlinearNewtonAfterTolerance",
"Nonlinear System",
""
)
obj.addProperty(
"App::PropertyFloat",
"NonlinearTolerance",
"Nonlinear System",
""
"App::PropertyFloat", "NonlinearNewtonAfterTolerance", "Nonlinear System", ""
)
obj.addProperty("App::PropertyFloat", "NonlinearTolerance", "Nonlinear System", "")
obj.addProperty(
"App::PropertyFloatConstraint",
"RelaxationFactor",
@@ -73,7 +65,7 @@ class Proxy(linear.Proxy):
(
"Value below 1.0 might be necessary to achieve convergence\n"
"Typical values are in the range [0.3, 1.0]"
)
),
)
obj.NonlinearIterations = (20, 1, int(1e6), 10)
@@ -90,4 +82,5 @@ class Proxy(linear.Proxy):
class ViewProxy(linear.ViewProxy):
pass
## @}

View File

@@ -78,10 +78,10 @@ _TYPE_STRING = "String"
_TYPE_FILE = "File"
_TYPE_VARIABLE = "Variable"
WARN = "\"Warn\""
IGNORE = "\"Ignore\""
ABORT = "\"Abort\""
SILENT = "\"Silent\""
WARN = '"Warn"'
IGNORE = '"Ignore"'
ABORT = '"Abort"'
SILENT = '"Silent"'
def createSection(name):
@@ -104,7 +104,7 @@ def isValid(section):
return section.name in _VALID_SECTIONS
class Builder(object):
class Builder:
_ACTIVE_SOLVERS = "Active Solvers"
@@ -191,7 +191,7 @@ class Builder(object):
return iter(allSections)
class Sif(object):
class Sif:
_CHECK_KEYWORDS = "Check Keywords"
_HEADER = "Header"
@@ -238,7 +238,7 @@ class Sif(object):
stream.write('"%s"' % value)
class Section(object):
class Section:
def __init__(self, name):
self.name = name
@@ -274,7 +274,7 @@ class FileAttr(str):
pass
class _Writer(object):
class _Writer:
def __init__(self, idManager, sections, stream):
self._idMgr = idManager
@@ -282,8 +282,7 @@ class _Writer(object):
self._stream = stream
def write(self):
sortedSections = sorted(
self._sections, key=lambda s: s.priority, reverse=True)
sortedSections = sorted(self._sections, key=lambda s: s.priority, reverse=True)
for s in sortedSections:
self._writeSection(s)
self._stream.write(_NEWLINE)
@@ -332,10 +331,7 @@ class _Writer(object):
return next(it)
def _isCollection(self, data):
return (
not isinstance(data, str)
and isinstance(data, collections.abc.Iterable)
)
return not isinstance(data, str) and isinstance(data, collections.abc.Iterable)
def _checkScalar(self, dataType):
if issubclass(dataType, int):
@@ -359,7 +355,7 @@ class _Writer(object):
self._stream.write(_WHITESPACE)
# check if we have a variable string
if attrType is _TYPE_STRING:
if data.startswith('Variable'):
if data.startswith("Variable"):
attrType = _TYPE_VARIABLE
if attrType is not _TYPE_VARIABLE:
self._stream.write(attrType)
@@ -367,7 +363,7 @@ class _Writer(object):
output = self._preprocess(data, type(data))
# in case of a variable the output must be without the quatoation marks
if attrType is _TYPE_VARIABLE:
output = output.lstrip('\"')
output = output.lstrip('"')
# we cannot use rstrip because there are two subsequent " at the end
output = output[:-1]
self._stream.write(output)
@@ -382,7 +378,7 @@ class _Writer(object):
self._stream.write(_WHITESPACE)
# check if we have a variable string
if attrType is _TYPE_STRING:
if data.startswith('Variable'):
if data.startswith("Variable"):
attrType = _TYPE_VARIABLE
if attrType is not _TYPE_VARIABLE:
self._stream.write(attrType)
@@ -391,7 +387,7 @@ class _Writer(object):
output = self._preprocess(val, type(val))
# in case of a variable the output must be without the quatoation marks
if attrType is _TYPE_VARIABLE:
output = output.lstrip('\"')
output = output.lstrip('"')
# we cannot use rstrip because there are two subsequent " at the end
output = output[:-1]
self._stream.write(output)
@@ -442,7 +438,7 @@ class _Writer(object):
return self._getSifDataType(dataType)
class _IdManager(object):
class _IdManager:
def __init__(self, firstId=1):
self._pool = dict()
@@ -460,4 +456,5 @@ class _IdManager(object):
self.setId(section)
return self._ids[section]
## @}

View File

@@ -50,16 +50,22 @@ from femtools import femutils
if FreeCAD.GuiUp:
import FemGui
COORDINATE_SYSTEM = ["Cartesian", "Cartesian 1D", "Cartesian 2D", "Cartesian 3D",
"Polar 2D", "Polar 3D",
"Cylindric", "Cylindric Symmetric",
"Axi Symmetric"]
COORDINATE_SYSTEM = [
"Cartesian",
"Cartesian 1D",
"Cartesian 2D",
"Cartesian 3D",
"Polar 2D",
"Polar 3D",
"Cylindric",
"Cylindric Symmetric",
"Axi Symmetric",
]
SIMULATION_TYPE = ["Scanning", "Steady State", "Transient"]
def create(doc, name="ElmerSolver"):
return femutils.createObject(
doc, name, Proxy, ViewProxy)
return femutils.createObject(doc, name, Proxy, ViewProxy)
class Proxy(solverbase.Proxy):
@@ -80,14 +86,9 @@ class Proxy(solverbase.Proxy):
}
def __init__(self, obj):
super(Proxy, self).__init__(obj)
super().__init__(obj)
obj.addProperty(
"App::PropertyEnumeration",
"CoordinateSystem",
"Coordinate System",
""
)
obj.addProperty("App::PropertyEnumeration", "CoordinateSystem", "Coordinate System", "")
obj.CoordinateSystem = COORDINATE_SYSTEM
obj.CoordinateSystem = "Cartesian"
@@ -95,7 +96,7 @@ class Proxy(solverbase.Proxy):
"App::PropertyIntegerConstraint",
"BDFOrder",
"Timestepping",
"Order of time stepping method 'BDF'"
"Order of time stepping method 'BDF'",
)
# according to the Elmer manual recommended is order 2
# possible ranage is 1 - 5
@@ -105,7 +106,7 @@ class Proxy(solverbase.Proxy):
"App::PropertyIntegerList",
"OutputIntervals",
"Timestepping",
"After how many time steps a result file is output"
"After how many time steps a result file is output",
)
obj.OutputIntervals = [1]
@@ -113,10 +114,7 @@ class Proxy(solverbase.Proxy):
"App::PropertyIntegerList",
"TimestepIntervals",
"Timestepping",
(
"List of times if 'Simulation Type'\n"
"is either 'Scanning' or 'Transient'"
)
("List of times if 'Simulation Type'\nis either 'Scanning' or 'Transient'"),
)
obj.addProperty(
"App::PropertyFloatList",
@@ -125,19 +123,14 @@ class Proxy(solverbase.Proxy):
(
"List of time steps sizes if 'Simulation Type'\n"
"is either 'Scanning' or 'Transient'"
)
),
)
# there is no universal default, it all depends on the analysis, however
# we have to set something and set 10 seconds in steps of 0.1s
obj.TimestepIntervals = [100]
obj.TimestepSizes = [0.1]
obj.addProperty(
"App::PropertyEnumeration",
"SimulationType",
"Type",
""
)
obj.addProperty("App::PropertyEnumeration", "SimulationType", "Type", "")
obj.SimulationType = SIMULATION_TYPE
obj.SimulationType = "Steady State"
@@ -145,7 +138,7 @@ class Proxy(solverbase.Proxy):
"App::PropertyInteger",
"SteadyStateMaxIterations",
"Type",
"Maximal steady state iterations"
"Maximal steady state iterations",
)
obj.SteadyStateMaxIterations = 1
@@ -153,42 +146,26 @@ class Proxy(solverbase.Proxy):
"App::PropertyInteger",
"SteadyStateMinIterations",
"Type",
"Minimal steady state iterations"
"Minimal steady state iterations",
)
obj.SteadyStateMinIterations = 0
obj.addProperty(
"App::PropertyLink",
"ElmerResult",
"Base",
"",
4 | 8
)
obj.addProperty("App::PropertyLink", "ElmerResult", "Base", "", 4 | 8)
obj.addProperty(
"App::PropertyLinkList",
"ElmerTimeResults",
"Base",
"",
4 | 8
)
obj.addProperty("App::PropertyLinkList", "ElmerTimeResults", "Base", "", 4 | 8)
obj.addProperty(
"App::PropertyLink",
"ElmerOutput",
"Base",
"",
4 | 8
)
obj.addProperty("App::PropertyLink", "ElmerOutput", "Base", "", 4 | 8)
def createMachine(self, obj, directory, testmode=False):
return run.Machine(
solver=obj, directory=directory,
solver=obj,
directory=directory,
check=tasks.Check(),
prepare=tasks.Prepare(),
solve=tasks.Solve(),
results=tasks.Results(),
testmode=testmode)
testmode=testmode,
)
def createEquation(self, doc, eqId):
return self._EQUATIONS[eqId].create(doc)
@@ -201,7 +178,7 @@ class Proxy(solverbase.Proxy):
def edit(self, directory):
pattern = os.path.join(directory, "case.sif")
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
FreeCAD.Console.PrintMessage(f"{pattern}\n")
f = glob.glob(pattern)[0]
FemGui.open(f)
@@ -212,4 +189,5 @@ class ViewProxy(solverbase.ViewProxy):
def getIcon(self):
return ":/icons/FEM_SolverElmer.svg"
## @}

View File

@@ -47,7 +47,7 @@ class Check(run.Check):
def run(self):
self.pushStatus("Checking analysis...\n")
if (self.check_mesh_exists()):
if self.check_mesh_exists():
self.checkMeshType()
self.check_material_exists()
self.checkEquations()
@@ -55,9 +55,7 @@ class Check(run.Check):
def checkMeshType(self):
mesh = membertools.get_single_member(self.analysis, "Fem::FemMeshObject")
if not femutils.is_of_type(mesh, "Fem::FemMeshGmsh"):
self.report.error(
"Unsupported type of mesh. "
"Mesh must be created with gmsh.")
self.report.error("Unsupported type of mesh. Mesh must be created with gmsh.")
self.fail()
return False
return True
@@ -65,9 +63,7 @@ class Check(run.Check):
def checkEquations(self):
equations = self.solver.Group
if not equations:
self.report.error(
"Solver has no equations. "
"Add at least one equation.")
self.report.error("Solver has no equations. Add at least one equation.")
self.fail()
@@ -77,14 +73,13 @@ class Prepare(run.Prepare):
# TODO print working dir to report console
self.pushStatus("Preparing input files...\n")
num_cores = settings.get_cores("ElmerGrid")
self.pushStatus("Number of CPU cores to be used for the solver run: {}\n"
.format(num_cores))
self.pushStatus(f"Number of CPU cores to be used for the solver run: {num_cores}\n")
if self.testmode:
# test mode: neither gmsh, nor elmergrid nor elmersolver binaries needed
FreeCAD.Console.PrintMessage("Machine testmode: {}\n".format(self.testmode))
FreeCAD.Console.PrintMessage(f"Machine testmode: {self.testmode}\n")
w = writer.Writer(self.solver, self.directory, True)
else:
FreeCAD.Console.PrintLog("Machine testmode: {}\n".format(self.testmode))
FreeCAD.Console.PrintLog(f"Machine testmode: {self.testmode}\n")
w = writer.Writer(self.solver, self.directory)
try:
w.write_solver_input()
@@ -93,7 +88,7 @@ class Prepare(run.Prepare):
except writer.WriteError as e:
self.report.error(str(e))
self.fail()
except IOError:
except OSError:
self.report.error("Can't access working directory.")
self.fail()
@@ -124,11 +119,10 @@ class Solve(run.Solve):
solvpath = os.path.split(binary)[0]
if os.path.isdir(solvpath):
os.environ["ELMER_HOME"] = solvpath
os.environ["LD_LIBRARY_PATH"] = "$LD_LIBRARY_PATH:{}/modules".format(solvpath)
os.environ["LD_LIBRARY_PATH"] = f"$LD_LIBRARY_PATH:{solvpath}/modules"
# different call depending if with multithreading or not
num_cores = settings.get_cores("ElmerSolver")
self.pushStatus("Number of CPU cores to be used for the solver run: {}\n"
.format(num_cores))
self.pushStatus(f"Number of CPU cores to be used for the solver run: {num_cores}\n")
args = []
if num_cores > 1:
if system() != "Windows":
@@ -143,14 +137,11 @@ class Solve(run.Solve):
cwd=self.directory,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
startupinfo=femutils.startProgramInfo("hide")
startupinfo=femutils.startProgramInfo("hide"),
)
else:
self._process = subprocess.Popen(
args,
cwd=self.directory,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
args, cwd=self.directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
self.signalAbort.add(self._process.terminate)
output = self._observeSolver(self._process)
@@ -172,7 +163,8 @@ class Solve(run.Solve):
def _createOutput(self):
self.solver.ElmerOutput = self.analysis.Document.addObject(
"App::TextDocument", self.solver.Name + "Output")
"App::TextDocument", self.solver.Name + "Output"
)
self.solver.ElmerOutput.Label = self.solver.Label + "Output"
# App::TextDocument has no Attribute ReadOnly
# TODO check if the attribute has been removed from App::TextDocument
@@ -194,11 +186,7 @@ class Solve(run.Solve):
FrequencyList = []
for line in OutputList:
LineList = line.split(" ")
if (
len(LineList) > 1
and LineList[0] == "EigenSolve:"
and LineList[1] == "Computed"
):
if len(LineList) > 1 and LineList[0] == "EigenSolve:" and LineList[1] == "Computed":
# we found a result and take now the next LineList[2] lines
modeCount = int(LineList[2])
modeNumber = modeCount
@@ -219,9 +207,7 @@ class Solve(run.Solve):
# now we can perform the calculation
eigenFreq = cmath.sqrt(eigenFreq) / (2 * cmath.pi)
# create an output line
FrequencyList.append(
"Mode {}: {} Hz".format(modeNumber - modeCount + 1, eigenFreq.real)
)
FrequencyList.append(f"Mode {modeNumber - modeCount + 1}: {eigenFreq.real} Hz")
modeCount = modeCount - 1
if modeNumber > 0:
# push the results and append to output
@@ -274,7 +260,8 @@ class Results(run.Results):
def _createResults(self):
self.solver.ElmerResult = self.analysis.Document.addObject(
"Fem::FemPostPipeline", self.solver.Name + "Result")
"Fem::FemPostPipeline", self.solver.Name + "Result"
)
self.solver.ElmerResult.Label = self.solver.ElmerResult.Name
self.solver.ElmerResult.ViewObject.SelectionStyle = "BoundBox"
self.analysis.addObject(self.solver.ElmerResult)
@@ -290,7 +277,7 @@ class Results(run.Results):
self.pushStatus("\nNo result file was created.\n")
self.fail()
return
pvdFile = open(pvdFilePath, "r")
pvdFile = open(pvdFilePath)
# read all lines
pvdContent = pvdFile.readlines()
# skip header and footer line and evaluate all lines
@@ -299,7 +286,7 @@ class Results(run.Results):
# so .split("\"") gives as 2nd the time and as 7th the filename
for i in range(0, len(pvdContent) - 2):
# get time
lineArray = pvdContent[i + 1].split("\"")
lineArray = pvdContent[i + 1].split('"')
time = float(lineArray[1])
filename = os.path.join(self.directory, lineArray[7])
if os.path.isfile(filename):
@@ -317,7 +304,7 @@ class Results(run.Results):
# but not the shape and bar coloring
self.solver.ElmerTimeResults[i].ViewObject.updateColorBars()
else:
self.pushStatus("\nResult file for time {} is missing.\n".format(time))
self.pushStatus(f"\nResult file for time {time} is missing.\n")
self.fail()
return
self.solver.Document.recompute()
@@ -328,9 +315,7 @@ class Results(run.Results):
# FreeCAD would replaces dots in object names with underscores, thus do the same
newName = self.solver.Name + "_" + str(time).replace(".", "_") + "_" + "Result"
if counter > len(self.solver.ElmerTimeResults):
pipeline = self.analysis.Document.addObject(
"Fem::FemPostPipeline", newName
)
pipeline = self.analysis.Document.addObject("Fem::FemPostPipeline", newName)
# App::PropertyLinkList does not support append
# thus we have to use a temporary list to append
tmplist = self.solver.ElmerTimeResults
@@ -343,9 +328,7 @@ class Results(run.Results):
# store current list before removing object since object removal will automatically
# remove entry from self.solver.ElmerTimeResults
tmplist = self.solver.ElmerTimeResults
self.analysis.Document.removeObject(
self.solver.ElmerTimeResults[counter - 1].Name
)
self.analysis.Document.removeObject(self.solver.ElmerTimeResults[counter - 1].Name)
tmplist[counter - 1] = self.analysis.Document.addObject(
"Fem::FemPostPipeline", newName
)
@@ -354,10 +337,7 @@ class Results(run.Results):
def _finishTimeResults(self, time, counter):
# we purposely use the decimal dot in the label
self.solver.ElmerTimeResults[counter].Label = (
"{}_{}_Result"
.format(self.solver.Name, time)
)
self.solver.ElmerTimeResults[counter].Label = f"{self.solver.Name}_{time}_Result"
self.solver.ElmerTimeResults[counter].ViewObject.OnTopWhenSelected = True
self.analysis.addObject(self.solver.ElmerTimeResults[counter])
# to assure the user sees something, set the default to Surface
@@ -389,4 +369,5 @@ class Results(run.Results):
self.fail()
return postPath
## @}

View File

@@ -63,25 +63,30 @@ _STARTINFO_NAME = "ELMERSOLVER_STARTINFO"
_SIF_NAME = "case.sif"
_ELMERGRID_IFORMAT = "8"
_ELMERGRID_OFORMAT = "2"
_COORDS_NON_MAGNETO_2D = ["Polar 2D", "Polar 3D", "Cartesian 3D",
"Cylindric", "Cylindric Symmetric"]
_COORDS_NON_MAGNETO_2D = [
"Polar 2D",
"Polar 3D",
"Cartesian 3D",
"Cylindric",
"Cylindric Symmetric",
]
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))))
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):
class Writer:
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))
Console.PrintMessage(f"Write elmer input files to: {self.directory}\n")
self.testmode = testmode
self._usedVarNames = set()
self._builder = sifio.Builder()
@@ -184,8 +189,9 @@ class Writer(object):
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))
"Elmer sif-file writing is done in Standard FreeCAD units.\n".format(
Units.listSchemas(self.unit_schema)
)
)
def getFromUi(self, value, unit, outputDim):
@@ -216,8 +222,7 @@ class Writer(object):
self._exportToUnv(groups, mesh, unvPath)
if self.testmode:
Console.PrintMessage(
"Solver Elmer testmode, ElmerGrid will not be used. "
"It might not be installed.\n"
"Solver Elmer testmode, ElmerGrid will not be used. It might not be installed.\n"
)
else:
binary = settings.get_binary("ElmerGrid")
@@ -226,29 +231,23 @@ class Writer(object):
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]
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")
args, stdout=subprocess.DEVNULL, startupinfo=femutils.startProgramInfo("hide")
)
else:
subprocess.call(args, stdout=subprocess.DEVNULL)
if num_cores > 1:
args = argsBasic
args.extend(["-partdual", "-metiskway", str(num_cores),
"-out", self.directory])
args.extend(["-partdual", "-metiskway", str(num_cores), "-out", self.directory])
if system() == "Windows":
subprocess.call(
args,
stdout=subprocess.DEVNULL,
startupinfo=femutils.startProgramInfo("hide")
startupinfo=femutils.startProgramInfo("hide"),
)
else:
subprocess.call(args, stdout=subprocess.DEVNULL)
@@ -281,10 +280,10 @@ class Writer(object):
tools.write_geo()
if self.testmode:
Console.PrintMessage(
"Solver Elmer testmode, Gmsh will not be used. "
"It might not be installed.\n"
"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()
@@ -307,14 +306,13 @@ class Writer(object):
permittivity = float(objs[0].VacuumPermittivity.getValueAs("F/m"))
# since the base unit of FC is in mm, we must scale it to get plain SI
permittivity = permittivity * 1e-9
Console.PrintLog("Overwriting vacuum permittivity with: {}\n".format(permittivity))
Console.PrintLog(f"Overwriting vacuum permittivity with: {permittivity}\n")
self.constsdef["PermittivityOfVacuum"] = "{} {}".format(permittivity, "F/m")
self.handled(objs[0])
elif len(objs) > 1:
Console.PrintError(
"More than one permittivity constant overwriting objects ({} objs). "
"The permittivity constant overwriting is ignored.\n"
.format(len(objs))
"The permittivity constant overwriting is ignored.\n".format(len(objs))
)
def _handleSimulation(self):
@@ -336,18 +334,9 @@ class Writer(object):
self._simulation("Coordinate Scaling", 0.001)
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("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("BDF Order", self.solver.BDFOrder)
self._simulation("Output Intervals", self.solver.OutputIntervals)
self._simulation("Timestep Intervals", self.solver.TimestepIntervals)
@@ -359,10 +348,7 @@ class Writer(object):
# updates older simulations
if not hasattr(self.solver, "CoordinateSystem"):
solver.addProperty(
"App::PropertyEnumeration",
"CoordinateSystem",
"Coordinate System",
""
"App::PropertyEnumeration", "CoordinateSystem", "Coordinate System", ""
)
solver.CoordinateSystem = solverClass.COORDINATE_SYSTEM
solver.CoordinateSystem = "Cartesian"
@@ -371,32 +357,21 @@ class Writer(object):
"App::PropertyIntegerConstraint",
"BDFOrder",
"Timestepping",
"Order of time stepping method 'BDF'"
"Order of time stepping method 'BDF'",
)
solver.BDFOrder = (2, 1, 5, 1)
if not hasattr(self.solver, "ElmerTimeResults"):
solver.addProperty(
"App::PropertyLinkList",
"ElmerTimeResults",
"Base",
"",
4 | 8
)
solver.addProperty("App::PropertyLinkList", "ElmerTimeResults", "Base", "", 4 | 8)
if not hasattr(self.solver, "OutputIntervals"):
solver.addProperty(
"App::PropertyIntegerList",
"OutputIntervals",
"Timestepping",
"After how many time steps a result file is output"
"After how many time steps a result file is output",
)
solver.OutputIntervals = [1]
if not hasattr(self.solver, "SimulationType"):
solver.addProperty(
"App::PropertyEnumeration",
"SimulationType",
"Type",
""
)
solver.addProperty("App::PropertyEnumeration", "SimulationType", "Type", "")
solver.SimulationType = solverClass.SIMULATION_TYPE
solver.SimulationType = "Steady State"
if not hasattr(self.solver, "TimestepIntervals"):
@@ -407,7 +382,7 @@ class Writer(object):
(
"List of maximum optimization rounds if 'Simulation Type'\n"
"is either 'Scanning' or 'Transient'"
)
),
)
solver.TimestepIntervals = [100]
if not hasattr(self.solver, "TimestepSizes"):
@@ -418,7 +393,7 @@ class Writer(object):
(
"List of time steps of optimization if 'Simulation Type'\n"
"is either 'Scanning' or 'Transient'"
)
),
)
solver.TimestepSizes = [0.1]
@@ -622,8 +597,9 @@ class Writer(object):
raise WriteError(
"The coordinate setting '{}'\n is not "
"supported by the equation 'Magnetodynamic2D'.\n\n"
"Possible is:\n'Cartesian 2D' or 'Axi Symmetric'"
.format(self.solver.CoordinateSystem)
"Possible is:\n'Cartesian 2D' or 'Axi Symmetric'".format(
self.solver.CoordinateSystem
)
)
solverSection = MgDyn2D.getMagnetodynamic2DSolver(equation)
@@ -661,14 +637,14 @@ class Writer(object):
"Disable the linear system.\n"
"Only use for special cases\n"
"and consult the Elmer docs."
)
),
)
if not hasattr(equation, "IdrsParameter"):
equation.addProperty(
"App::PropertyIntegerConstraint",
"IdrsParameter",
"Linear System",
"Parameter for iterative method 'Idrs'"
"Parameter for iterative method 'Idrs'",
)
equation.IdrsParameter = (2, 1, 10, 1)
@@ -682,25 +658,17 @@ class Writer(object):
if equation.LinearSystemSolverDisabled is True:
s["Linear System Solver Disabled"] = equation.LinearSystemSolverDisabled
if equation.LinearSolverType == "Direct":
s["Linear System Direct Method"] = \
equation.LinearDirectMethod
s["Linear System Direct Method"] = equation.LinearDirectMethod
elif equation.LinearSolverType == "Iterative":
s["Linear System Iterative Method"] = \
equation.LinearIterativeMethod
s["Linear System Iterative Method"] = equation.LinearIterativeMethod
if equation.LinearIterativeMethod == "BiCGStabl":
s["BiCGstabl polynomial degree"] = \
equation.BiCGstablDegree
s["BiCGstabl polynomial degree"] = equation.BiCGstablDegree
if equation.LinearIterativeMethod == "Idrs":
s["Idrs Parameter"] = \
equation.IdrsParameter
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["Idrs Parameter"] = equation.IdrsParameter
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
@@ -711,8 +679,7 @@ class Writer(object):
equation.setExpression("NonlinearTolerance", str(equation.NonlinearTolerance))
if self._hasExpression(equation) != equation.NonlinearNewtonAfterTolerance:
equation.setExpression(
"NonlinearNewtonAfterTolerance",
str(equation.NonlinearNewtonAfterTolerance)
"NonlinearNewtonAfterTolerance", str(equation.NonlinearNewtonAfterTolerance)
)
def createNonlinearSolver(self, equation):
@@ -721,16 +688,11 @@ class Writer(object):
# 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
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
# -------------------------------------------------------------------------------------------
@@ -777,7 +739,7 @@ class Writer(object):
return density
def _hasExpression(self, equation):
for (obj, exp) in equation.ExpressionEngine:
for obj, exp in equation.ExpressionEngine:
if obj == equation:
return exp
return None
@@ -824,10 +786,7 @@ class Writer(object):
# To get it back in the original size we let Elmer scale it back
s["Coordinate Scaling Revert"] = True
s["Equation"] = "ResultOutput"
if (
self.solver.SimulationType == "Scanning"
or self.solver.SimulationType == "Transient"
):
if self.solver.SimulationType == "Scanning" or self.solver.SimulationType == "Transient":
# we must execute the post solver every time we output a result
# therefore we must use the same as self.solver.OutputIntervals
s["Exec Intervals"] = self.solver.OutputIntervals
@@ -892,4 +851,5 @@ class Writer(object):
class WriteError(Exception):
pass
## @}