FEM: reformat codebase
This commit is contained in:
@@ -45,8 +45,7 @@ ANALYSIS_TYPES = ["static", "frequency", "thermomech", "check", "buckling"]
|
||||
|
||||
|
||||
def create(doc, name="SolverCalculiX"):
|
||||
return femutils.createObject(
|
||||
doc, name, Proxy, ViewProxy)
|
||||
return femutils.createObject(doc, name, Proxy, ViewProxy)
|
||||
|
||||
|
||||
class _BaseSolverCalculix:
|
||||
@@ -58,21 +57,16 @@ class _BaseSolverCalculix:
|
||||
obj.AnalysisType = temp_analysis_type
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Analysis type {} not found. Standard is used.\n"
|
||||
.format(temp_analysis_type)
|
||||
f"Analysis type {temp_analysis_type} not found. Standard is used.\n"
|
||||
)
|
||||
obj.AnalysisType = ANALYSIS_TYPES[0]
|
||||
|
||||
self.add_attributes(obj)
|
||||
|
||||
|
||||
def add_attributes(self, obj):
|
||||
if not hasattr(obj, "AnalysisType"):
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"AnalysisType",
|
||||
"Fem",
|
||||
"Type of the analysis"
|
||||
"App::PropertyEnumeration", "AnalysisType", "Fem", "Type of the analysis"
|
||||
)
|
||||
obj.AnalysisType = ANALYSIS_TYPES
|
||||
obj.AnalysisType = ANALYSIS_TYPES[0]
|
||||
@@ -83,7 +77,7 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyEnumeration",
|
||||
"GeometricalNonlinearity",
|
||||
"Fem",
|
||||
"Set geometrical nonlinearity"
|
||||
"Set geometrical nonlinearity",
|
||||
)
|
||||
obj.GeometricalNonlinearity = choices_geom_nonlinear
|
||||
obj.GeometricalNonlinearity = choices_geom_nonlinear[0]
|
||||
@@ -94,7 +88,7 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyEnumeration",
|
||||
"MaterialNonlinearity",
|
||||
"Fem",
|
||||
"Set material nonlinearity"
|
||||
"Set material nonlinearity",
|
||||
)
|
||||
obj.MaterialNonlinearity = choices_material_nonlinear
|
||||
obj.MaterialNonlinearity = choices_material_nonlinear[0]
|
||||
@@ -104,7 +98,7 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyIntegerConstraint",
|
||||
"EigenmodesCount",
|
||||
"Fem",
|
||||
"Number of modes for frequency calculations"
|
||||
"Number of modes for frequency calculations",
|
||||
)
|
||||
obj.EigenmodesCount = (10, 1, 100, 1)
|
||||
|
||||
@@ -113,7 +107,7 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyFloatConstraint",
|
||||
"EigenmodeLowLimit",
|
||||
"Fem",
|
||||
"Low frequency limit for eigenmode calculations"
|
||||
"Low frequency limit for eigenmode calculations",
|
||||
)
|
||||
obj.EigenmodeLowLimit = (0.0, 0.0, 1000000.0, 10000.0)
|
||||
|
||||
@@ -122,20 +116,19 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyFloatConstraint",
|
||||
"EigenmodeHighLimit",
|
||||
"Fem",
|
||||
"High frequency limit for eigenmode calculations"
|
||||
"High frequency limit for eigenmode calculations",
|
||||
)
|
||||
obj.EigenmodeHighLimit = (1000000.0, 0.0, 1000000.0, 10000.0)
|
||||
|
||||
if not hasattr(obj, "IterationsMaximum"):
|
||||
help_string_IterationsMaximum = (
|
||||
"Maximum Number of iterations "
|
||||
"in each time step before stopping jobs"
|
||||
"Maximum Number of iterations in each time step before stopping jobs"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyIntegerConstraint",
|
||||
"IterationsMaximum",
|
||||
"Fem",
|
||||
help_string_IterationsMaximum
|
||||
help_string_IterationsMaximum,
|
||||
)
|
||||
obj.IterationsMaximum = 2000
|
||||
|
||||
@@ -148,43 +141,29 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyIntegerConstraint",
|
||||
"BucklingFactors",
|
||||
"Fem",
|
||||
"Calculates the lowest buckling modes to the corresponding buckling factors"
|
||||
"Calculates the lowest buckling modes to the corresponding buckling factors",
|
||||
)
|
||||
obj.BucklingFactors = 1
|
||||
|
||||
if not hasattr(obj, "TimeInitialStep"):
|
||||
obj.addProperty(
|
||||
"App::PropertyFloatConstraint",
|
||||
"TimeInitialStep",
|
||||
"Fem",
|
||||
"Initial time steps"
|
||||
"App::PropertyFloatConstraint", "TimeInitialStep", "Fem", "Initial time steps"
|
||||
)
|
||||
obj.TimeInitialStep = 0.01
|
||||
|
||||
if not hasattr(obj, "TimeEnd"):
|
||||
obj.addProperty(
|
||||
"App::PropertyFloatConstraint",
|
||||
"TimeEnd",
|
||||
"Fem",
|
||||
"End time analysis"
|
||||
)
|
||||
obj.addProperty("App::PropertyFloatConstraint", "TimeEnd", "Fem", "End time analysis")
|
||||
obj.TimeEnd = 1.0
|
||||
|
||||
if not hasattr(obj, "TimeMinimumStep"):
|
||||
obj.addProperty(
|
||||
"App::PropertyFloatConstraint",
|
||||
"TimeMinimumStep",
|
||||
"Fem",
|
||||
"Minimum time step"
|
||||
"App::PropertyFloatConstraint", "TimeMinimumStep", "Fem", "Minimum time step"
|
||||
)
|
||||
obj.TimeMinimumStep = 0.00001
|
||||
|
||||
if not hasattr(obj, "TimeMaximumStep"):
|
||||
obj.addProperty(
|
||||
"App::PropertyFloatConstraint",
|
||||
"TimeMaximumStep",
|
||||
"Fem",
|
||||
"Maximum time step"
|
||||
"App::PropertyFloatConstraint", "TimeMaximumStep", "Fem", "Maximum time step"
|
||||
)
|
||||
obj.TimeMaximumStep = 1.0
|
||||
|
||||
@@ -193,7 +172,7 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyBool",
|
||||
"ThermoMechSteadyState",
|
||||
"Fem",
|
||||
"Choose between steady state thermo mech or transient thermo mech analysis"
|
||||
"Choose between steady state thermo mech or transient thermo mech analysis",
|
||||
)
|
||||
obj.ThermoMechSteadyState = True
|
||||
|
||||
@@ -202,16 +181,13 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyBool",
|
||||
"IterationsControlParameterTimeUse",
|
||||
"Fem",
|
||||
"Use the user defined time incrementation control parameter"
|
||||
"Use the user defined time incrementation control parameter",
|
||||
)
|
||||
obj.IterationsControlParameterTimeUse = False
|
||||
|
||||
if not hasattr(obj, "SplitInputWriter"):
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"SplitInputWriter",
|
||||
"Fem",
|
||||
"Split writing of ccx input file"
|
||||
"App::PropertyBool", "SplitInputWriter", "Fem", "Split writing of ccx input file"
|
||||
)
|
||||
obj.SplitInputWriter = False
|
||||
|
||||
@@ -234,28 +210,26 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyString",
|
||||
"IterationsControlParameterIter",
|
||||
"Fem",
|
||||
"User defined time incrementation iterations control parameter"
|
||||
"User defined time incrementation iterations control parameter",
|
||||
)
|
||||
obj.IterationsControlParameterIter = control_parameter_iterations
|
||||
|
||||
if not hasattr(obj, "IterationsControlParameterCutb"):
|
||||
control_parameter_cutback = (
|
||||
"{D_f},{D_C},{D_B},{D_A},{D_S},{D_H},{D_D},{W_G}".format(
|
||||
D_f=0.25,
|
||||
D_C=0.5,
|
||||
D_B=0.75,
|
||||
D_A=0.85,
|
||||
D_S="",
|
||||
D_H="",
|
||||
D_D=1.5,
|
||||
W_G="",
|
||||
)
|
||||
control_parameter_cutback = "{D_f},{D_C},{D_B},{D_A},{D_S},{D_H},{D_D},{W_G}".format(
|
||||
D_f=0.25,
|
||||
D_C=0.5,
|
||||
D_B=0.75,
|
||||
D_A=0.85,
|
||||
D_S="",
|
||||
D_H="",
|
||||
D_D=1.5,
|
||||
W_G="",
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"IterationsControlParameterCutb",
|
||||
"Fem",
|
||||
"User defined time incrementation cutbacks control parameter"
|
||||
"User defined time incrementation cutbacks control parameter",
|
||||
)
|
||||
obj.IterationsControlParameterCutb = control_parameter_cutback
|
||||
|
||||
@@ -268,7 +242,7 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyBool",
|
||||
"IterationsUserDefinedIncrementations",
|
||||
"Fem",
|
||||
stringIterationsUserDefinedIncrementations
|
||||
stringIterationsUserDefinedIncrementations,
|
||||
)
|
||||
obj.IterationsUserDefinedIncrementations = False
|
||||
|
||||
@@ -281,7 +255,7 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyBool",
|
||||
"IterationsUserDefinedTimeStepLength",
|
||||
"Fem",
|
||||
help_string_IterationsUserDefinedTimeStepLength
|
||||
help_string_IterationsUserDefinedTimeStepLength,
|
||||
)
|
||||
obj.IterationsUserDefinedTimeStepLength = False
|
||||
|
||||
@@ -292,13 +266,10 @@ class _BaseSolverCalculix:
|
||||
"pardiso",
|
||||
"spooles",
|
||||
"iterativescaling",
|
||||
"iterativecholesky"
|
||||
"iterativecholesky",
|
||||
]
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"MatrixSolverType",
|
||||
"Fem",
|
||||
"Type of solver to use"
|
||||
"App::PropertyEnumeration", "MatrixSolverType", "Fem", "Type of solver to use"
|
||||
)
|
||||
obj.MatrixSolverType = known_ccx_solver_types
|
||||
obj.MatrixSolverType = known_ccx_solver_types[0]
|
||||
@@ -308,7 +279,7 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyBool",
|
||||
"BeamShellResultOutput3D",
|
||||
"Fem",
|
||||
"Output 3D results for 1D and 2D analysis "
|
||||
"Output 3D results for 1D and 2D analysis ",
|
||||
)
|
||||
obj.BeamShellResultOutput3D = True
|
||||
|
||||
@@ -317,7 +288,7 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyBool",
|
||||
"BeamReducedIntegration",
|
||||
"Fem",
|
||||
"Set to True to use beam elements with reduced integration"
|
||||
"Set to True to use beam elements with reduced integration",
|
||||
)
|
||||
obj.BeamReducedIntegration = True
|
||||
|
||||
@@ -326,48 +297,33 @@ class _BaseSolverCalculix:
|
||||
"App::PropertyIntegerConstraint",
|
||||
"OutputFrequency",
|
||||
"Fem",
|
||||
"Set the output frequency in increments"
|
||||
"Set the output frequency in increments",
|
||||
)
|
||||
obj.OutputFrequency = 1
|
||||
|
||||
if not hasattr(obj, "ModelSpace"):
|
||||
model_space_types = [
|
||||
"3D",
|
||||
"plane stress",
|
||||
"plane strain",
|
||||
"axisymmetric"
|
||||
]
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"ModelSpace",
|
||||
"Fem",
|
||||
"Type of model space"
|
||||
)
|
||||
model_space_types = ["3D", "plane stress", "plane strain", "axisymmetric"]
|
||||
obj.addProperty("App::PropertyEnumeration", "ModelSpace", "Fem", "Type of model space")
|
||||
obj.ModelSpace = model_space_types
|
||||
|
||||
if not hasattr(obj, "ThermoMechType"):
|
||||
thermomech_types = [
|
||||
"coupled",
|
||||
"uncoupled",
|
||||
"pure heat transfer"
|
||||
]
|
||||
thermomech_types = ["coupled", "uncoupled", "pure heat transfer"]
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"ThermoMechType",
|
||||
"Fem",
|
||||
"Type of thermomechanical analysis"
|
||||
"Type of thermomechanical analysis",
|
||||
)
|
||||
obj.ThermoMechType = thermomech_types
|
||||
|
||||
|
||||
class Proxy(solverbase.Proxy, _BaseSolverCalculix):
|
||||
"""The Fem::FemSolver's Proxy python type, add solver specific properties
|
||||
"""
|
||||
"""The Fem::FemSolver's Proxy python type, add solver specific properties"""
|
||||
|
||||
Type = "Fem::SolverCalculix"
|
||||
|
||||
def __init__(self, obj):
|
||||
super(Proxy, self).__init__(obj)
|
||||
super().__init__(obj)
|
||||
obj.Proxy = self
|
||||
self.add_attributes(obj)
|
||||
|
||||
@@ -376,19 +332,21 @@ class Proxy(solverbase.Proxy, _BaseSolverCalculix):
|
||||
|
||||
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 editSupported(self):
|
||||
return True
|
||||
|
||||
def edit(self, directory):
|
||||
pattern = os.path.join(directory, "*.inp")
|
||||
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
|
||||
FreeCAD.Console.PrintMessage(f"{pattern}\n")
|
||||
f = glob.glob(pattern)[0]
|
||||
FemGui.open(f)
|
||||
|
||||
|
||||
@@ -56,15 +56,16 @@ class Check(run.Check):
|
||||
|
||||
# workaround use Calculix ccxtools pre checks
|
||||
from femtools.checksanalysis import check_member_for_solver_calculix
|
||||
|
||||
message = check_member_for_solver_calculix(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
membertools.get_mesh_to_solve(self.analysis)[0],
|
||||
membertools.AnalysisMember(self.analysis)
|
||||
membertools.AnalysisMember(self.analysis),
|
||||
)
|
||||
if message:
|
||||
text = "CalculiX can not be started...\n"
|
||||
self.report.error("{}{}".format(text, message))
|
||||
self.report.error(f"{text}{message}")
|
||||
self.fail()
|
||||
return
|
||||
|
||||
@@ -94,7 +95,7 @@ class Prepare(run.Prepare):
|
||||
mesh_obj,
|
||||
meshdatagetter.member,
|
||||
self.directory,
|
||||
meshdatagetter.mat_geo_sets
|
||||
meshdatagetter.mat_geo_sets,
|
||||
)
|
||||
path = w.write_solver_input()
|
||||
# report to user if task succeeded
|
||||
@@ -124,7 +125,7 @@ class Solve(run.Solve):
|
||||
[binary, "-i", _inputFileName],
|
||||
cwd=self.directory,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.signalAbort.add(self._process.terminate)
|
||||
# output = self._observeSolver(self._process)
|
||||
@@ -138,8 +139,7 @@ class Solve(run.Solve):
|
||||
class Results(run.Results):
|
||||
|
||||
def run(self):
|
||||
prefs = FreeCAD.ParamGet(
|
||||
"User parameter:BaseApp/Preferences/Mod/Fem/General")
|
||||
prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General")
|
||||
if not prefs.GetBool("KeepResultsOnReRun", False):
|
||||
self.purge_results()
|
||||
self.load_results()
|
||||
@@ -163,32 +163,22 @@ class Results(run.Results):
|
||||
self.load_ccxdat_results()
|
||||
|
||||
def load_ccxfrd_results(self):
|
||||
frd_result_file = os.path.join(
|
||||
self.directory, _inputFileName + ".frd")
|
||||
frd_result_file = os.path.join(self.directory, _inputFileName + ".frd")
|
||||
if os.path.isfile(frd_result_file):
|
||||
result_name_prefix = "CalculiX_" + self.solver.AnalysisType + "_"
|
||||
importCcxFrdResults.importFrd(
|
||||
frd_result_file, self.analysis, result_name_prefix)
|
||||
importCcxFrdResults.importFrd(frd_result_file, self.analysis, result_name_prefix)
|
||||
else:
|
||||
# TODO: use solver framework status message system
|
||||
FreeCAD.Console.PrintError(
|
||||
"FEM: No results found at {}!\n"
|
||||
.format(frd_result_file)
|
||||
)
|
||||
FreeCAD.Console.PrintError(f"FEM: No results found at {frd_result_file}!\n")
|
||||
self.fail()
|
||||
|
||||
def load_ccxdat_results(self):
|
||||
dat_result_file = os.path.join(
|
||||
self.directory, _inputFileName + ".dat")
|
||||
dat_result_file = os.path.join(self.directory, _inputFileName + ".dat")
|
||||
if os.path.isfile(dat_result_file):
|
||||
mode_frequencies = importCcxDatResults.import_dat(
|
||||
dat_result_file, self.analysis)
|
||||
mode_frequencies = importCcxDatResults.import_dat(dat_result_file, self.analysis)
|
||||
else:
|
||||
# TODO: use solver framework status message system
|
||||
FreeCAD.Console.PrintError(
|
||||
"FEM: No results found at {}!\n"
|
||||
.format(dat_result_file)
|
||||
)
|
||||
FreeCAD.Console.PrintError(f"FEM: No results found at {dat_result_file}!\n")
|
||||
self.fail()
|
||||
if mode_frequencies:
|
||||
for m in membertools.get_member(self.analysis, "Fem::FemResultObject"):
|
||||
@@ -197,4 +187,5 @@ class Results(run.Results):
|
||||
if m.Eigenmode == mf["eigenmode"]:
|
||||
m.EigenmodeFrequency = mf["frequency"]
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#/***************************************************************************
|
||||
# /***************************************************************************
|
||||
# * Copyright (c) 2024 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
|
||||
# * *
|
||||
# * This file is part of FreeCAD. *
|
||||
@@ -58,12 +58,12 @@ def get_after_write_constraint():
|
||||
|
||||
|
||||
def write_meshdata_constraint(f, femobj, bodyheatsource_obj, ccxwriter):
|
||||
f.write("*ELSET,ELSET={}\n".format(bodyheatsource_obj.Name))
|
||||
f.write(f"*ELSET,ELSET={bodyheatsource_obj.Name}\n")
|
||||
if isinstance(femobj["FEMElements"], str):
|
||||
f.write("{}\n".format(femobj["FEMElements"]))
|
||||
else:
|
||||
for e in femobj["FEMElements"]:
|
||||
f.write("{},\n".format(e))
|
||||
f.write(f"{e},\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, bodyheatsource_obj, ccxwriter):
|
||||
@@ -75,7 +75,9 @@ def write_constraint(f, femobj, bodyheatsource_obj, ccxwriter):
|
||||
ref_sub_obj = ref[1][0]
|
||||
density = None
|
||||
for mat in ccxwriter.member.mats_linear:
|
||||
mat_ref = [*itertools.chain(*[itertools.product([i[0]],i[1]) for i in mat["Object"].References])]
|
||||
mat_ref = [
|
||||
*itertools.chain(*[itertools.product([i[0]], i[1]) for i in mat["Object"].References])
|
||||
]
|
||||
if (ref_feat, ref_sub_obj) in mat_ref:
|
||||
density = FreeCAD.Units.Quantity(mat["Object"].Material["Density"])
|
||||
break
|
||||
@@ -95,9 +97,5 @@ def write_constraint(f, femobj, bodyheatsource_obj, ccxwriter):
|
||||
heat = bodyheatsource_obj.TotalPower / FreeCAD.Units.Quantity(volume, "mm^3")
|
||||
# write to file
|
||||
f.write("*DFLUX\n")
|
||||
f.write(
|
||||
"{},BF,{:.13G}\n".format(
|
||||
bodyheatsource_obj.Name, heat.getValueAs("t/(mm*s^3)").Value
|
||||
)
|
||||
)
|
||||
f.write("{},BF,{:.13G}\n".format(bodyheatsource_obj.Name, heat.getValueAs("t/(mm*s^3)").Value))
|
||||
f.write("\n")
|
||||
|
||||
@@ -60,12 +60,12 @@ def get_after_write_constraint():
|
||||
|
||||
|
||||
def write_meshdata_constraint(f, femobj, centrif_obj, ccxwriter):
|
||||
f.write("*ELSET,ELSET={}\n".format(centrif_obj.Name))
|
||||
f.write(f"*ELSET,ELSET={centrif_obj.Name}\n")
|
||||
if isinstance(femobj["FEMElements"], str):
|
||||
f.write("{}\n".format(femobj["FEMElements"]))
|
||||
else:
|
||||
for e in femobj["FEMElements"]:
|
||||
f.write("{},\n".format(e))
|
||||
f.write(f"{e},\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, centrif_obj, ccxwriter):
|
||||
@@ -85,22 +85,21 @@ def write_constraint(f, femobj, centrif_obj, ccxwriter):
|
||||
else: # no line found, set default
|
||||
# TODO: No test at all in the writer
|
||||
# they should all be before in prechecks
|
||||
location = FreeCAD.Vector(0., 0., 0.)
|
||||
direction = FreeCAD.Vector(0., 0., 1.)
|
||||
location = FreeCAD.Vector(0.0, 0.0, 0.0)
|
||||
direction = FreeCAD.Vector(0.0, 0.0, 1.0)
|
||||
|
||||
# write to file
|
||||
f.write("*DLOAD\n")
|
||||
f.write(
|
||||
"{},CENTRIF,{:.13G},{:.13G},{:.13G},{:.13G},{:.13G},{:.13G},{:.13G}\n"
|
||||
.format(
|
||||
"{},CENTRIF,{:.13G},{:.13G},{:.13G},{:.13G},{:.13G},{:.13G},{:.13G}\n".format(
|
||||
centrif_obj.Name,
|
||||
(2. * math.pi * float(centrif_obj.RotationFrequency.getValueAs("1/s"))) ** 2,
|
||||
(2.0 * math.pi * float(centrif_obj.RotationFrequency.getValueAs("1/s"))) ** 2,
|
||||
location.x,
|
||||
location.y,
|
||||
location.z,
|
||||
direction.x,
|
||||
direction.y,
|
||||
direction.z
|
||||
direction.z,
|
||||
)
|
||||
)
|
||||
f.write("\n")
|
||||
|
||||
@@ -27,7 +27,7 @@ __url__ = "https://www.freecad.org"
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return "all" # write for all analysis types
|
||||
return "all" # write for all analysis types
|
||||
|
||||
|
||||
def get_sets_name():
|
||||
@@ -56,13 +56,13 @@ def get_after_write_constraint():
|
||||
|
||||
def write_meshdata_constraint(f, femobj, contact_obj, ccxwriter):
|
||||
# slave DEP
|
||||
f.write("*SURFACE, NAME=DEP{}\n".format(contact_obj.Name))
|
||||
f.write(f"*SURFACE, NAME=DEP{contact_obj.Name}\n")
|
||||
for i in femobj["ContactSlaveFaces"]:
|
||||
f.write("{},S{}\n".format(i[0], i[1]))
|
||||
f.write(f"{i[0]},S{i[1]}\n")
|
||||
# master IND
|
||||
f.write("*SURFACE, NAME=IND{}\n".format(contact_obj.Name))
|
||||
f.write(f"*SURFACE, NAME=IND{contact_obj.Name}\n")
|
||||
for i in femobj["ContactMasterFaces"]:
|
||||
f.write("{},S{}\n".format(i[0], i[1]))
|
||||
f.write(f"{i[0]},S{i[1]}\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, contact_obj, ccxwriter):
|
||||
@@ -70,22 +70,22 @@ def write_constraint(f, femobj, contact_obj, ccxwriter):
|
||||
# floats read from ccx should use {:.13G}, see comment in writer module
|
||||
adjust = ""
|
||||
if contact_obj.Adjust.Value > 0:
|
||||
adjust = ", ADJUST={:.13G}".format(
|
||||
contact_obj.Adjust.getValueAs("mm").Value)
|
||||
adjust = ", ADJUST={:.13G}".format(contact_obj.Adjust.getValueAs("mm").Value)
|
||||
|
||||
f.write(
|
||||
"*CONTACT PAIR, INTERACTION=INT{}, TYPE=SURFACE TO SURFACE{}\n"
|
||||
.format(contact_obj.Name, adjust)
|
||||
"*CONTACT PAIR, INTERACTION=INT{}, TYPE=SURFACE TO SURFACE{}\n".format(
|
||||
contact_obj.Name, adjust
|
||||
)
|
||||
)
|
||||
ind_surf = "IND" + contact_obj.Name
|
||||
dep_surf = "DEP" + contact_obj.Name
|
||||
f.write("{}, {}\n".format(dep_surf, ind_surf))
|
||||
f.write("*SURFACE INTERACTION, NAME=INT{}\n".format(contact_obj.Name))
|
||||
f.write(f"{dep_surf}, {ind_surf}\n")
|
||||
f.write(f"*SURFACE INTERACTION, NAME=INT{contact_obj.Name}\n")
|
||||
f.write("*SURFACE BEHAVIOR, PRESSURE-OVERCLOSURE=LINEAR\n")
|
||||
slope = contact_obj.Slope.getValueAs("MPa/mm").Value
|
||||
f.write("{:.13G}\n".format(slope))
|
||||
f.write(f"{slope:.13G}\n")
|
||||
if contact_obj.Friction:
|
||||
f.write("*FRICTION\n")
|
||||
friction = contact_obj.FrictionCoefficient
|
||||
stick = contact_obj.StickSlope.getValueAs("MPa/mm").Value
|
||||
f.write("{:.13G}, {:.13G}\n".format(friction, stick))
|
||||
f.write(f"{friction:.13G}, {stick:.13G}\n")
|
||||
|
||||
@@ -29,7 +29,7 @@ import FreeCAD
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return "all" # write for all analysis types
|
||||
return "all" # write for all analysis types
|
||||
|
||||
|
||||
def get_sets_name():
|
||||
@@ -57,9 +57,9 @@ def get_after_write_constraint():
|
||||
|
||||
|
||||
def write_meshdata_constraint(f, femobj, disp_obj, ccxwriter):
|
||||
f.write("*NSET,NSET={}\n".format(disp_obj.Name))
|
||||
f.write(f"*NSET,NSET={disp_obj.Name}\n")
|
||||
for n in femobj["Nodes"]:
|
||||
f.write("{},\n".format(n))
|
||||
f.write(f"{n},\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, disp_obj, ccxwriter):
|
||||
@@ -68,7 +68,7 @@ def write_constraint(f, femobj, disp_obj, ccxwriter):
|
||||
|
||||
f.write("*BOUNDARY\n")
|
||||
if disp_obj.xFix:
|
||||
f.write("{},1\n".format(disp_obj.Name))
|
||||
f.write(f"{disp_obj.Name},1\n")
|
||||
elif not disp_obj.xFree:
|
||||
f.write(
|
||||
"{},1,1,{}\n".format(
|
||||
@@ -76,7 +76,7 @@ def write_constraint(f, femobj, disp_obj, ccxwriter):
|
||||
)
|
||||
)
|
||||
if disp_obj.yFix:
|
||||
f.write("{},2\n".format(disp_obj.Name))
|
||||
f.write(f"{disp_obj.Name},2\n")
|
||||
elif not disp_obj.yFree:
|
||||
f.write(
|
||||
"{},2,2,{}\n".format(
|
||||
@@ -84,7 +84,7 @@ def write_constraint(f, femobj, disp_obj, ccxwriter):
|
||||
)
|
||||
)
|
||||
if disp_obj.zFix:
|
||||
f.write("{},3\n".format(disp_obj.Name))
|
||||
f.write(f"{disp_obj.Name},3\n")
|
||||
elif not disp_obj.zFree:
|
||||
f.write(
|
||||
"{},3,3,{}\n".format(
|
||||
@@ -94,7 +94,7 @@ def write_constraint(f, femobj, disp_obj, ccxwriter):
|
||||
|
||||
if ccxwriter.member.geos_beamsection or ccxwriter.member.geos_shellthickness:
|
||||
if disp_obj.rotxFix:
|
||||
f.write("{},4\n".format(disp_obj.Name))
|
||||
f.write(f"{disp_obj.Name},4\n")
|
||||
elif not disp_obj.rotxFree:
|
||||
f.write(
|
||||
"{},4,4,{}\n".format(
|
||||
@@ -102,7 +102,7 @@ def write_constraint(f, femobj, disp_obj, ccxwriter):
|
||||
)
|
||||
)
|
||||
if disp_obj.rotyFix:
|
||||
f.write("{},5\n".format(disp_obj.Name))
|
||||
f.write(f"{disp_obj.Name},5\n")
|
||||
elif not disp_obj.rotyFree:
|
||||
f.write(
|
||||
"{},5,5,{}\n".format(
|
||||
@@ -110,7 +110,7 @@ def write_constraint(f, femobj, disp_obj, ccxwriter):
|
||||
)
|
||||
)
|
||||
if disp_obj.rotzFix:
|
||||
f.write("{},6\n".format(disp_obj.Name))
|
||||
f.write(f"{disp_obj.Name},6\n")
|
||||
elif not disp_obj.rotzFree:
|
||||
f.write(
|
||||
"{},6,6,{}\n".format(
|
||||
|
||||
@@ -27,7 +27,7 @@ __url__ = "https://www.freecad.org"
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return "all" # write for all analysis types
|
||||
return "all" # write for all analysis types
|
||||
|
||||
|
||||
def get_sets_name():
|
||||
@@ -55,37 +55,29 @@ def get_after_write_constraint():
|
||||
|
||||
|
||||
def write_meshdata_constraint(f, femobj, fix_obj, ccxwriter):
|
||||
if (
|
||||
ccxwriter.femmesh.Volumes
|
||||
and (
|
||||
len(ccxwriter.member.geos_shellthickness) > 0
|
||||
or len(ccxwriter.member.geos_beamsection) > 0
|
||||
)
|
||||
if ccxwriter.femmesh.Volumes and (
|
||||
len(ccxwriter.member.geos_shellthickness) > 0 or len(ccxwriter.member.geos_beamsection) > 0
|
||||
):
|
||||
if len(femobj["NodesSolid"]) > 0:
|
||||
f.write("*NSET,NSET={}Solid\n".format(fix_obj.Name))
|
||||
f.write(f"*NSET,NSET={fix_obj.Name}Solid\n")
|
||||
for n in femobj["NodesSolid"]:
|
||||
f.write("{},\n".format(n))
|
||||
f.write(f"{n},\n")
|
||||
if len(femobj["NodesFaceEdge"]) > 0:
|
||||
f.write("*NSET,NSET={}FaceEdge\n".format(fix_obj.Name))
|
||||
f.write(f"*NSET,NSET={fix_obj.Name}FaceEdge\n")
|
||||
for n in femobj["NodesFaceEdge"]:
|
||||
f.write("{},\n".format(n))
|
||||
f.write(f"{n},\n")
|
||||
else:
|
||||
f.write("*NSET,NSET=" + fix_obj.Name + "\n")
|
||||
for n in femobj["Nodes"]:
|
||||
f.write("{},\n".format(n))
|
||||
f.write(f"{n},\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, fix_obj, ccxwriter):
|
||||
|
||||
# floats read from ccx should use {:.13G}, see comment in writer module
|
||||
|
||||
if (
|
||||
ccxwriter.femmesh.Volumes
|
||||
and (
|
||||
len(ccxwriter.member.geos_shellthickness) > 0
|
||||
or len(ccxwriter.member.geos_beamsection) > 0
|
||||
)
|
||||
if ccxwriter.femmesh.Volumes and (
|
||||
len(ccxwriter.member.geos_shellthickness) > 0 or len(ccxwriter.member.geos_beamsection) > 0
|
||||
):
|
||||
if len(femobj["NodesSolid"]) > 0:
|
||||
f.write("*BOUNDARY\n")
|
||||
|
||||
@@ -53,8 +53,7 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter):
|
||||
|
||||
ccxwriter.FluidInletoutlet_ele = []
|
||||
ccxwriter.fluid_inout_nodes_file = join(
|
||||
ccxwriter.dir_name,
|
||||
"{}_inout_nodes.txt".format(ccxwriter.mesh_name)
|
||||
ccxwriter.dir_name, f"{ccxwriter.mesh_name}_inout_nodes.txt"
|
||||
)
|
||||
|
||||
def get_fluidsection_inoutlet_obj_if_setdata(matgeoset):
|
||||
@@ -64,29 +63,20 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter):
|
||||
and "fluidsection_obj" in matgeoset # fluid mesh
|
||||
):
|
||||
fluidsec_obj = matgeoset["fluidsection_obj"]
|
||||
if (
|
||||
fluidsec_obj.SectionType == "Liquid"
|
||||
and (
|
||||
fluidsec_obj.LiquidSectionType == "PIPE INLET"
|
||||
or fluidsec_obj.LiquidSectionType == "PIPE OUTLET"
|
||||
)
|
||||
if fluidsec_obj.SectionType == "Liquid" and (
|
||||
fluidsec_obj.LiquidSectionType == "PIPE INLET"
|
||||
or fluidsec_obj.LiquidSectionType == "PIPE OUTLET"
|
||||
):
|
||||
return fluidsec_obj
|
||||
return None
|
||||
|
||||
def is_fluidsection_inoutlet_setnames_possible(mat_geo_sets):
|
||||
for matgeoset in mat_geo_sets:
|
||||
if (
|
||||
matgeoset["ccx_elset"]
|
||||
and "fluidsection_obj" in matgeoset # fluid mesh
|
||||
):
|
||||
if matgeoset["ccx_elset"] and "fluidsection_obj" in matgeoset: # fluid mesh
|
||||
fluidsec_obj = matgeoset["fluidsection_obj"]
|
||||
if (
|
||||
fluidsec_obj.SectionType == "Liquid"
|
||||
and (
|
||||
fluidsec_obj.LiquidSectionType == "PIPE INLET"
|
||||
or fluidsec_obj.LiquidSectionType == "PIPE OUTLET"
|
||||
)
|
||||
if fluidsec_obj.SectionType == "Liquid" and (
|
||||
fluidsec_obj.LiquidSectionType == "PIPE INLET"
|
||||
or fluidsec_obj.LiquidSectionType == "PIPE OUTLET"
|
||||
):
|
||||
return True
|
||||
return False
|
||||
@@ -101,15 +91,15 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter):
|
||||
counter = 0
|
||||
for elid in matgeoset["ccx_elset"]:
|
||||
counter = counter + 1
|
||||
if (elsetchanged is False) \
|
||||
and (fluidsec_obj.LiquidSectionType == "PIPE INLET"):
|
||||
if (elsetchanged is False) and (fluidsec_obj.LiquidSectionType == "PIPE INLET"):
|
||||
# 3rd index is to track which line nr the element is defined
|
||||
ccxwriter.FluidInletoutlet_ele.append(
|
||||
[str(elid), fluidsec_obj.LiquidSectionType, 0]
|
||||
)
|
||||
elsetchanged = True
|
||||
elif (fluidsec_obj.LiquidSectionType == "PIPE OUTLET") \
|
||||
and (counter == len(matgeoset["ccx_elset"])):
|
||||
elif (fluidsec_obj.LiquidSectionType == "PIPE OUTLET") and (
|
||||
counter == len(matgeoset["ccx_elset"])
|
||||
):
|
||||
# 3rd index is to track which line nr the element is defined
|
||||
ccxwriter.FluidInletoutlet_ele.append(
|
||||
[str(elid), fluidsec_obj.LiquidSectionType, 0]
|
||||
@@ -123,9 +113,7 @@ def handle_fluidsection_liquid_inlet_outlet(inpfile, ccxwriter):
|
||||
# for split input file the main file is just closed and reopend even if not needed
|
||||
inpfile.close()
|
||||
meshtools.use_correct_fluidinout_ele_def(
|
||||
ccxwriter.FluidInletoutlet_ele,
|
||||
ccxwriter.femmesh_file,
|
||||
ccxwriter.fluid_inout_nodes_file
|
||||
ccxwriter.FluidInletoutlet_ele, ccxwriter.femmesh_file, ccxwriter.fluid_inout_nodes_file
|
||||
)
|
||||
inpfile = codecs.open(ccxwriter.file_name, "a", encoding="utf-8")
|
||||
|
||||
@@ -146,13 +134,12 @@ def write_constraints_fluidsection(f, ccxwriter):
|
||||
f.write("\n***********************************************************\n")
|
||||
f.write("** FluidSection constraints\n")
|
||||
if os.path.exists(ccxwriter.fluid_inout_nodes_file):
|
||||
inout_nodes_file = open(ccxwriter.fluid_inout_nodes_file, "r")
|
||||
inout_nodes_file = open(ccxwriter.fluid_inout_nodes_file)
|
||||
lines = inout_nodes_file.readlines()
|
||||
inout_nodes_file.close()
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
"1DFlow inout nodes file not found: {}\n"
|
||||
.format(ccxwriter.fluid_inout_nodes_file)
|
||||
f"1DFlow inout nodes file not found: {ccxwriter.fluid_inout_nodes_file}\n"
|
||||
)
|
||||
# get nodes
|
||||
ccxwriter.get_constraints_fluidsection_nodes()
|
||||
@@ -170,12 +157,11 @@ def write_constraints_fluidsection(f, ccxwriter):
|
||||
b = line.split(",")
|
||||
if int(b[0]) == n and b[3] == "PIPE INLET\n":
|
||||
# degree of freedom 2 is for defining pressure
|
||||
f.write("{},{},{},{}\n".format(
|
||||
b[0],
|
||||
"2",
|
||||
"2",
|
||||
fluidsection_obj.InletPressure
|
||||
))
|
||||
f.write(
|
||||
"{},{},{},{}\n".format(
|
||||
b[0], "2", "2", fluidsection_obj.InletPressure
|
||||
)
|
||||
)
|
||||
if fluidsection_obj.InletFlowRateActive is True:
|
||||
f.write("*BOUNDARY,MASS FLOW \n")
|
||||
for n in femobj["Nodes"]:
|
||||
@@ -184,12 +170,11 @@ def write_constraints_fluidsection(f, ccxwriter):
|
||||
if int(b[0]) == n and b[3] == "PIPE INLET\n":
|
||||
# degree of freedom 1 is for defining flow rate
|
||||
# factor applied to convert unit from kg/s to t/s
|
||||
f.write("{},{},{},{}\n".format(
|
||||
b[1],
|
||||
"1",
|
||||
"1",
|
||||
fluidsection_obj.InletFlowRate * 0.001
|
||||
))
|
||||
f.write(
|
||||
"{},{},{},{}\n".format(
|
||||
b[1], "1", "1", fluidsection_obj.InletFlowRate * 0.001
|
||||
)
|
||||
)
|
||||
elif fluidsection_obj.LiquidSectionType == "PIPE OUTLET":
|
||||
f.write("**Fluid Section Outlet \n")
|
||||
if fluidsection_obj.OutletPressureActive is True:
|
||||
@@ -199,12 +184,11 @@ def write_constraints_fluidsection(f, ccxwriter):
|
||||
b = line.split(",")
|
||||
if int(b[0]) == n and b[3] == "PIPE OUTLET\n":
|
||||
# degree of freedom 2 is for defining pressure
|
||||
f.write("{},{},{},{}\n".format(
|
||||
b[0],
|
||||
"2",
|
||||
"2",
|
||||
fluidsection_obj.OutletPressure
|
||||
))
|
||||
f.write(
|
||||
"{},{},{},{}\n".format(
|
||||
b[0], "2", "2", fluidsection_obj.OutletPressure
|
||||
)
|
||||
)
|
||||
if fluidsection_obj.OutletFlowRateActive is True:
|
||||
f.write("*BOUNDARY,MASS FLOW \n")
|
||||
for n in femobj["Nodes"]:
|
||||
@@ -213,9 +197,8 @@ def write_constraints_fluidsection(f, ccxwriter):
|
||||
if int(b[0]) == n and b[3] == "PIPE OUTLET\n":
|
||||
# degree of freedom 1 is for defining flow rate
|
||||
# factor applied to convert unit from kg/s to t/s
|
||||
f.write("{},{},{},{}\n".format(
|
||||
b[1],
|
||||
"1",
|
||||
"1",
|
||||
fluidsection_obj.OutletFlowRate * 0.001
|
||||
))
|
||||
f.write(
|
||||
"{},{},{},{}\n".format(
|
||||
b[1], "1", "1", fluidsection_obj.OutletFlowRate * 0.001
|
||||
)
|
||||
)
|
||||
|
||||
@@ -51,18 +51,18 @@ def write_meshdata_constraint(f, femobj, force_obj, ccxwriter):
|
||||
# be careful with raising the tolerance, a big load would have an impact
|
||||
# but compared to the real direction the impact would be small again
|
||||
for ref_shape in femobj["NodeLoadTable"]:
|
||||
f.write("** {}\n".format(ref_shape[0]))
|
||||
f.write(f"** {ref_shape[0]}\n")
|
||||
for n in sorted(ref_shape[1]):
|
||||
node_load = ref_shape[1][n]
|
||||
# the loads in ref_shape[1][n] are without unit
|
||||
if abs(direction_vec.x) > dir_zero_tol:
|
||||
v1 = "{:.13G}".format((direction_vec.x * node_load).Value)
|
||||
f.write("{},1,{}\n".format(n, v1))
|
||||
v1 = f"{(direction_vec.x * node_load).Value:.13G}"
|
||||
f.write(f"{n},1,{v1}\n")
|
||||
if abs(direction_vec.y) > dir_zero_tol:
|
||||
v2 = "{:.13G}".format((direction_vec.y * node_load).Value)
|
||||
f.write("{},2,{}\n".format(n, v2))
|
||||
v2 = f"{(direction_vec.y * node_load).Value:.13G}"
|
||||
f.write(f"{n},2,{v2}\n")
|
||||
if abs(direction_vec.z) > dir_zero_tol:
|
||||
v3 = "{:.13G}".format((direction_vec.z * node_load).Value)
|
||||
f.write("{},3,{}\n".format(n, v3))
|
||||
v3 = f"{(direction_vec.z * node_load).Value:.13G}"
|
||||
f.write(f"{n},3,{v3}\n")
|
||||
f.write("\n")
|
||||
f.write("\n")
|
||||
|
||||
@@ -51,33 +51,26 @@ def write_meshdata_constraint(f, femobj, heatflux_obj, ccxwriter):
|
||||
heatflux_facetype = "F"
|
||||
# SvdW: add factor to force heatflux to units system of t/mm/s/K
|
||||
heatflux_values = "{:.13G},{:.13G}".format(
|
||||
heatflux_obj.AmbientTemp,
|
||||
heatflux_obj.FilmCoef * 0.001
|
||||
heatflux_obj.AmbientTemp, heatflux_obj.FilmCoef * 0.001
|
||||
)
|
||||
|
||||
elif heatflux_obj.ConstraintType == "Radiation":
|
||||
heatflux_key_word = "RADIATE"
|
||||
heatflux_facetype = "R"
|
||||
heatflux_values = "{:.13G},{:.13G}".format(
|
||||
heatflux_obj.AmbientTemp,
|
||||
heatflux_obj.Emissivity
|
||||
heatflux_obj.AmbientTemp, heatflux_obj.Emissivity
|
||||
)
|
||||
|
||||
elif heatflux_obj.ConstraintType == "DFlux":
|
||||
heatflux_key_word = "DFLUX"
|
||||
heatflux_facetype = "S"
|
||||
heatflux_values = "{:.13G}".format(heatflux_obj.DFlux * 0.001)
|
||||
heatflux_values = f"{heatflux_obj.DFlux * 0.001:.13G}"
|
||||
|
||||
f.write("*{}\n".format(heatflux_key_word))
|
||||
f.write(f"*{heatflux_key_word}\n")
|
||||
for ref_shape in femobj["HeatFluxFaceTable"]:
|
||||
elem_string = ref_shape[0]
|
||||
face_table = ref_shape[1]
|
||||
f.write("** Heat flux on face {}\n".format(elem_string))
|
||||
f.write(f"** Heat flux on face {elem_string}\n")
|
||||
for i in face_table:
|
||||
# OvG: Only write out the VolumeIDs linked to a particular face
|
||||
f.write("{},{}{},{}\n".format(
|
||||
i[0],
|
||||
heatflux_facetype,
|
||||
i[1],
|
||||
heatflux_values
|
||||
))
|
||||
f.write(f"{i[0]},{heatflux_facetype}{i[1]},{heatflux_values}\n")
|
||||
|
||||
@@ -48,10 +48,11 @@ def write_constraint(f, femobj, inittemp_obj, ccxwriter):
|
||||
|
||||
# floats read from ccx should use {:.13G}, see comment in writer module
|
||||
|
||||
f.write("{},{}\n".format(
|
||||
ccxwriter.ccx_nall,
|
||||
Units.Quantity(inittemp_obj.initialTemperature.getValueAs("K"))
|
||||
))
|
||||
f.write(
|
||||
"{},{}\n".format(
|
||||
ccxwriter.ccx_nall, Units.Quantity(inittemp_obj.initialTemperature.getValueAs("K"))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# Should only be one object in the analysis
|
||||
|
||||
@@ -30,7 +30,7 @@ from femmesh import meshtools
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return "all" # write for all analysis types
|
||||
return "all" # write for all analysis types
|
||||
|
||||
|
||||
def get_sets_name():
|
||||
@@ -69,16 +69,18 @@ def write_meshdata_constraint(f, femobj, fric_obj, ccxwriter):
|
||||
# Thus call write_node_sets_constraints_planerotation has to be
|
||||
# after constraint fixed and constraint displacement
|
||||
l_nodes = femobj["Nodes"]
|
||||
f.write("*NSET,NSET={}\n".format(fric_obj.Name))
|
||||
f.write(f"*NSET,NSET={fric_obj.Name}\n")
|
||||
# Code to extract nodes and coordinates on the PlaneRotation support face
|
||||
nodes_coords = []
|
||||
for node in l_nodes:
|
||||
nodes_coords.append((
|
||||
node,
|
||||
ccxwriter.femnodes_mesh[node].x,
|
||||
ccxwriter.femnodes_mesh[node].y,
|
||||
ccxwriter.femnodes_mesh[node].z
|
||||
))
|
||||
nodes_coords.append(
|
||||
(
|
||||
node,
|
||||
ccxwriter.femnodes_mesh[node].x,
|
||||
ccxwriter.femnodes_mesh[node].y,
|
||||
ccxwriter.femnodes_mesh[node].z,
|
||||
)
|
||||
)
|
||||
node_planerotation = meshtools.get_three_non_colinear_nodes(nodes_coords)
|
||||
for i in range(len(l_nodes)):
|
||||
if l_nodes[i] not in node_planerotation:
|
||||
@@ -93,7 +95,7 @@ def write_meshdata_constraint(f, femobj, fric_obj, ccxwriter):
|
||||
MPC = node_planerotation[i]
|
||||
MPC_nodes.append(MPC)
|
||||
for i in range(len(MPC_nodes)):
|
||||
f.write("{},\n".format(MPC_nodes[i]))
|
||||
f.write(f"{MPC_nodes[i]},\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, fric_obj, ccxwriter):
|
||||
@@ -101,4 +103,4 @@ def write_constraint(f, femobj, fric_obj, ccxwriter):
|
||||
# floats read from ccx should use {:.13G}, see comment in writer module
|
||||
|
||||
f.write("*MPC\n")
|
||||
f.write("PLANE,{}\n".format(fric_obj.Name))
|
||||
f.write(f"PLANE,{fric_obj.Name}\n")
|
||||
|
||||
@@ -27,6 +27,7 @@ __url__ = "https://www.freecad.org"
|
||||
|
||||
import FreeCAD
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return ["buckling", "static", "thermomech"]
|
||||
|
||||
@@ -60,12 +61,12 @@ def write_meshdata_constraint(f, femobj, prs_obj, ccxwriter):
|
||||
f.write("** " + ref_shape[0] + "\n")
|
||||
for face, fno in ref_shape[1]:
|
||||
if fno > 0: # solid mesh face
|
||||
f.write("{},P{},{}\n".format(face, fno, press_rev))
|
||||
f.write(f"{face},P{fno},{press_rev}\n")
|
||||
# on shell mesh face: fno == 0
|
||||
# normal of element face == face normal
|
||||
elif fno == 0:
|
||||
f.write("{},P,{}\n".format(face, press_rev))
|
||||
f.write(f"{face},P,{press_rev}\n")
|
||||
# on shell mesh face: fno == -1
|
||||
# normal of element face opposite direction face normal
|
||||
elif fno == -1:
|
||||
f.write("{},P,{}\n".format(face, -1 * press_rev))
|
||||
f.write(f"{face},P,{-1 * press_rev}\n")
|
||||
|
||||
@@ -27,7 +27,7 @@ __url__ = "https://www.freecadweb.org"
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return "all" # write for all analysis types
|
||||
return "all" # write for all analysis types
|
||||
|
||||
|
||||
def get_sets_name():
|
||||
@@ -58,7 +58,7 @@ def write_meshdata_constraint(f, femobj, rb_obj, ccxwriter):
|
||||
|
||||
f.write("*NSET,NSET=" + rb_obj.Name + "\n")
|
||||
for n in femobj["Nodes"]:
|
||||
f.write("{},\n".format(n))
|
||||
f.write(f"{n},\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, rb_obj, ccxwriter):
|
||||
@@ -66,19 +66,18 @@ def write_constraint(f, femobj, rb_obj, ccxwriter):
|
||||
rb_obj_idx = ccxwriter.analysis.Group.index(rb_obj)
|
||||
node_count = ccxwriter.mesh_object.FemMesh.NodeCount
|
||||
# factor 2 is to prevent conflict with other rigid body constraint
|
||||
ref_node_idx = node_count + 2*rb_obj_idx + 1
|
||||
rot_node_idx = node_count + 2*rb_obj_idx + 2
|
||||
ref_node_idx = node_count + 2 * rb_obj_idx + 1
|
||||
rot_node_idx = node_count + 2 * rb_obj_idx + 2
|
||||
|
||||
f.write("*NODE\n")
|
||||
f.write("{},{},{},{}\n".format(ref_node_idx, *rb_obj.ReferenceNode))
|
||||
f.write("{},{},{},{}\n".format(rot_node_idx, *rb_obj.ReferenceNode))
|
||||
|
||||
f.write("*NSET,NSET={}_RefNode\n".format(rb_obj.Name))
|
||||
f.write("{},\n".format(ref_node_idx))
|
||||
f.write("*NSET,NSET={}_RotNode\n".format(rb_obj.Name))
|
||||
f.write("{},\n".format(rot_node_idx))
|
||||
f.write(f"*NSET,NSET={rb_obj.Name}_RefNode\n")
|
||||
f.write(f"{ref_node_idx},\n")
|
||||
f.write(f"*NSET,NSET={rb_obj.Name}_RotNode\n")
|
||||
f.write(f"{rot_node_idx},\n")
|
||||
|
||||
kw_line = "*RIGID BODY, NSET={}, REF NODE={}, ROT NODE={}".format(rb_obj.Name, ref_node_idx, rot_node_idx)
|
||||
kw_line = f"*RIGID BODY, NSET={rb_obj.Name}, REF NODE={ref_node_idx}, ROT NODE={rot_node_idx}"
|
||||
|
||||
f.write(kw_line + "\n")
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import FreeCAD
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return "all" # write for all analysis types
|
||||
return "all" # write for all analysis types
|
||||
|
||||
|
||||
def get_sets_name():
|
||||
@@ -62,16 +62,16 @@ def write_constraint(f, femobj, rb_obj, ccxwriter):
|
||||
rb_obj_idx = ccxwriter.analysis.Group.index(rb_obj)
|
||||
node_count = ccxwriter.mesh_object.FemMesh.NodeCount
|
||||
# factor 2 is to prevent conflict with other rigid body constraint
|
||||
ref_node_idx = node_count + 2*rb_obj_idx + 1
|
||||
rot_node_idx = node_count + 2*rb_obj_idx + 2
|
||||
ref_node_idx = node_count + 2 * rb_obj_idx + 1
|
||||
rot_node_idx = node_count + 2 * rb_obj_idx + 2
|
||||
|
||||
def write_mode(mode, node, dof, constraint, load):
|
||||
if mode == "Constraint":
|
||||
f.write("*BOUNDARY\n")
|
||||
f.write("{},{},{},{:.13G}\n".format(node, dof, dof, constraint))
|
||||
f.write(f"{node},{dof},{dof},{constraint:.13G}\n")
|
||||
elif mode == "Load":
|
||||
f.write("*CLOAD\n")
|
||||
f.write("{},{},{:.13G}\n".format(node, dof, load))
|
||||
f.write(f"{node},{dof},{load:.13G}\n")
|
||||
|
||||
mode = [rb_obj.TranslationalModeX, rb_obj.TranslationalModeY, rb_obj.TranslationalModeZ]
|
||||
constraint = rb_obj.Displacement
|
||||
@@ -80,9 +80,8 @@ def write_constraint(f, femobj, rb_obj, ccxwriter):
|
||||
for i in range(3):
|
||||
write_mode(mode[i], ref_node_idx, i + 1, constraint[i], load[i].getValueAs("N").Value)
|
||||
|
||||
|
||||
mode = [rb_obj.RotationalModeX, rb_obj.RotationalModeY, rb_obj.RotationalModeZ]
|
||||
load = [rb_obj.MomentX,rb_obj.MomentY, rb_obj.MomentZ]
|
||||
load = [rb_obj.MomentX, rb_obj.MomentY, rb_obj.MomentZ]
|
||||
|
||||
# write rotation components according to rotational mode
|
||||
rot = rb_obj.Rotation
|
||||
@@ -96,5 +95,4 @@ def write_constraint(f, femobj, rb_obj, ccxwriter):
|
||||
for i in range(3):
|
||||
write_mode(mode[i], rot_node_idx, i + 1, constraint[i], load[i].getValueAs("N*mm").Value)
|
||||
|
||||
|
||||
f.write("\n")
|
||||
|
||||
@@ -27,7 +27,7 @@ __url__ = "https://www.freecad.org"
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return "all" # write for all analysis types
|
||||
return "all" # write for all analysis types
|
||||
|
||||
|
||||
def get_sets_name():
|
||||
@@ -55,9 +55,9 @@ def get_after_write_constraint():
|
||||
|
||||
|
||||
def write_meshdata_constraint(f, femobj, sectionprint_obj, ccxwriter):
|
||||
f.write("*SURFACE, NAME=SECTIONFACE{}\n".format(sectionprint_obj.Name))
|
||||
f.write(f"*SURFACE, NAME=SECTIONFACE{sectionprint_obj.Name}\n")
|
||||
for i in femobj["SectionPrintFaces"]:
|
||||
f.write("{},S{}\n".format(i[0], i[1]))
|
||||
f.write(f"{i[0]},S{i[1]}\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, sectionprint_obj, ccxwriter):
|
||||
@@ -73,7 +73,8 @@ def write_constraint(f, femobj, sectionprint_obj, ccxwriter):
|
||||
key = "DRAG"
|
||||
|
||||
f.write(
|
||||
"*SECTION PRINT, SURFACE=SECTIONFACE{}, NAME=SECTIONPRINT{}\n"
|
||||
.format(sectionprint_obj.Name, sectionprint_obj.Name)
|
||||
"*SECTION PRINT, SURFACE=SECTIONFACE{}, NAME=SECTIONPRINT{}\n".format(
|
||||
sectionprint_obj.Name, sectionprint_obj.Name
|
||||
)
|
||||
)
|
||||
f.write(key + "\n")
|
||||
|
||||
@@ -49,13 +49,14 @@ def write_constraint(f, femobj, selwei_obj, ccxwriter):
|
||||
f.write("*DLOAD\n")
|
||||
f.write(
|
||||
# elset, GRAV, magnitude, direction x, dir y ,dir z
|
||||
"{},GRAV,{:.13G},{:.13G},{:.13G},{:.13G}\n"
|
||||
.format(
|
||||
"{},GRAV,{:.13G},{:.13G},{:.13G},{:.13G}\n".format(
|
||||
ccxwriter.ccx_eall,
|
||||
selwei_obj.GravityAcceleration.getValueAs("mm/s^2").Value, # actual magnitude of gravity vector
|
||||
selwei_obj.GravityAcceleration.getValueAs(
|
||||
"mm/s^2"
|
||||
).Value, # actual magnitude of gravity vector
|
||||
selwei_obj.GravityDirection.x, # coordinate x of normalized gravity vector
|
||||
selwei_obj.GravityDirection.y, # y
|
||||
selwei_obj.GravityDirection.z # z
|
||||
selwei_obj.GravityDirection.z, # z
|
||||
)
|
||||
)
|
||||
f.write("\n")
|
||||
|
||||
@@ -42,9 +42,9 @@ def get_constraint_title():
|
||||
|
||||
|
||||
def write_meshdata_constraint(f, femobj, temp_obj, ccxwriter):
|
||||
f.write("*NSET,NSET={}\n".format(temp_obj.Name))
|
||||
f.write(f"*NSET,NSET={temp_obj.Name}\n")
|
||||
for n in femobj["Nodes"]:
|
||||
f.write("{},\n".format(n))
|
||||
f.write(f"{n},\n")
|
||||
|
||||
|
||||
def get_before_write_meshdata_constraint():
|
||||
@@ -82,7 +82,7 @@ def write_constraint(f, femobj, temp_obj, ccxwriter):
|
||||
f.write(
|
||||
"{},11,{}\n".format(
|
||||
temp_obj.Name,
|
||||
FreeCAD.Units.Quantity(temp_obj.CFlux.getValueAs("mW")) / NumberOfNodes
|
||||
FreeCAD.Units.Quantity(temp_obj.CFlux.getValueAs("mW")) / NumberOfNodes,
|
||||
)
|
||||
)
|
||||
f.write("\n")
|
||||
|
||||
@@ -30,7 +30,7 @@ from FreeCAD import Units, Vector
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return "all" # write for all analysis types
|
||||
return "all" # write for all analysis types
|
||||
|
||||
|
||||
def get_sets_name():
|
||||
@@ -59,13 +59,13 @@ def get_after_write_constraint():
|
||||
|
||||
def write_meshdata_constraint(f, femobj, tie_obj, ccxwriter):
|
||||
# slave DEP
|
||||
f.write("*SURFACE, NAME=TIE_DEP{}\n".format(tie_obj.Name))
|
||||
f.write(f"*SURFACE, NAME=TIE_DEP{tie_obj.Name}\n")
|
||||
for i in femobj["TieSlaveFaces"]:
|
||||
f.write("{},S{}\n".format(i[0], i[1]))
|
||||
f.write(f"{i[0]},S{i[1]}\n")
|
||||
# master IND
|
||||
f.write("*SURFACE, NAME=TIE_IND{}\n".format(tie_obj.Name))
|
||||
f.write(f"*SURFACE, NAME=TIE_IND{tie_obj.Name}\n")
|
||||
for i in femobj["TieMasterFaces"]:
|
||||
f.write("{},S{}\n".format(i[0], i[1]))
|
||||
f.write(f"{i[0]},S{i[1]}\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, tie_obj, ccxwriter):
|
||||
@@ -83,18 +83,20 @@ def write_constraint(f, femobj, tie_obj, ccxwriter):
|
||||
symmetry = ", CYCLIC SYMMETRY"
|
||||
|
||||
f.write(
|
||||
"*TIE, POSITION TOLERANCE={:.13G}{}{}, NAME=TIE{}\n"
|
||||
.format(tolerance, adjust, symmetry, tie_name)
|
||||
"*TIE, POSITION TOLERANCE={:.13G}{}{}, NAME=TIE{}\n".format(
|
||||
tolerance, adjust, symmetry, tie_name
|
||||
)
|
||||
)
|
||||
ind_surf = "TIE_IND{}".format(tie_name)
|
||||
dep_surf = "TIE_DEP{}".format(tie_name)
|
||||
f.write("{}, {}\n".format(dep_surf, ind_surf))
|
||||
ind_surf = f"TIE_IND{tie_name}"
|
||||
dep_surf = f"TIE_DEP{tie_name}"
|
||||
f.write(f"{dep_surf}, {ind_surf}\n")
|
||||
|
||||
# write CYCLIC SYMMETRY MODEL card
|
||||
if tie_obj.CyclicSymmetry:
|
||||
f.write(
|
||||
"*CYCLIC SYMMETRY MODEL, N={}, NGRAPH={}, TIE=TIE{}, ELSET=Eall\n"
|
||||
.format(tie_obj.Sectors, tie_obj.ConnectedSectors, tie_name)
|
||||
"*CYCLIC SYMMETRY MODEL, N={}, NGRAPH={}, TIE=TIE{}, ELSET=Eall\n".format(
|
||||
tie_obj.Sectors, tie_obj.ConnectedSectors, tie_name
|
||||
)
|
||||
)
|
||||
|
||||
# get symmetry axis points
|
||||
@@ -105,7 +107,4 @@ def write_constraint(f, femobj, tie_obj, ccxwriter):
|
||||
point_a = [set_unit(coord) for coord in vec_a]
|
||||
point_b = [set_unit(coord) for coord in vec_b]
|
||||
|
||||
f.write(
|
||||
"{:.13G},{:.13G},{:.13G},{:.13G},{:.13G},{:.13G}\n"
|
||||
.format(*point_a, *point_b)
|
||||
)
|
||||
f.write("{:.13G},{:.13G},{:.13G},{:.13G},{:.13G},{:.13G}\n".format(*point_a, *point_b))
|
||||
|
||||
@@ -32,7 +32,7 @@ from femtools import geomtools
|
||||
|
||||
|
||||
def get_analysis_types():
|
||||
return "all" # write for all analysis types
|
||||
return "all" # write for all analysis types
|
||||
|
||||
|
||||
def get_sets_name():
|
||||
@@ -61,11 +61,11 @@ def get_after_write_constraint():
|
||||
|
||||
def write_meshdata_constraint(f, femobj, trans_obj, ccxwriter):
|
||||
if trans_obj.TransformType == "Rectangular":
|
||||
f.write("*NSET,NSET=Rect{}\n".format(trans_obj.Name))
|
||||
f.write(f"*NSET,NSET=Rect{trans_obj.Name}\n")
|
||||
elif trans_obj.TransformType == "Cylindrical":
|
||||
f.write("*NSET,NSET=Cylin{}\n".format(trans_obj.Name))
|
||||
f.write(f"*NSET,NSET=Cylin{trans_obj.Name}\n")
|
||||
for n in femobj["Nodes"]:
|
||||
f.write("{},\n".format(n))
|
||||
f.write(f"{n},\n")
|
||||
|
||||
|
||||
def write_constraint(f, femobj, trans_obj, ccxwriter):
|
||||
@@ -86,16 +86,20 @@ def write_constraint(f, femobj, trans_obj, ccxwriter):
|
||||
base = trans_obj.BasePoint
|
||||
axis = trans_obj.Axis
|
||||
coords = list(base) + list(base + axis)
|
||||
f.write("*TRANSFORM, NSET={}{}, TYPE={}\n".format(
|
||||
trans_name,
|
||||
trans_obj.Name,
|
||||
trans_type,
|
||||
))
|
||||
f.write("{:.13G},{:.13G},{:.13G},{:.13G},{:.13G},{:.13G}\n".format(
|
||||
coords[0],
|
||||
coords[1],
|
||||
coords[2],
|
||||
coords[3],
|
||||
coords[4],
|
||||
coords[5],
|
||||
))
|
||||
f.write(
|
||||
"*TRANSFORM, NSET={}{}, TYPE={}\n".format(
|
||||
trans_name,
|
||||
trans_obj.Name,
|
||||
trans_type,
|
||||
)
|
||||
)
|
||||
f.write(
|
||||
"{:.13G},{:.13G},{:.13G},{:.13G},{:.13G},{:.13G}\n".format(
|
||||
coords[0],
|
||||
coords[1],
|
||||
coords[2],
|
||||
coords[3],
|
||||
coords[4],
|
||||
coords[5],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -43,9 +43,7 @@ def write_femelement_geometry(f, ccxwriter):
|
||||
# in CalxuliX called the 1direction
|
||||
# see meshtools.get_beam_main_axis_m(beam_direction, defined_angle)
|
||||
section_nor = "{:.13G}, {:.13G}, {:.13G}\n".format(
|
||||
beam_axis_m[0],
|
||||
beam_axis_m[1],
|
||||
beam_axis_m[2]
|
||||
beam_axis_m[0], beam_axis_m[1], beam_axis_m[2]
|
||||
)
|
||||
print(section_nor)
|
||||
if beamsec_obj.SectionType == "Rectangular":
|
||||
@@ -61,31 +59,19 @@ def write_femelement_geometry(f, ccxwriter):
|
||||
len_beam_axis_n = beamsec_obj.RectHeight.getValueAs("mm").Value
|
||||
len_beam_axis_m = beamsec_obj.RectWidth.getValueAs("mm").Value
|
||||
section_type = ", SECTION=RECT"
|
||||
section_geo = "{:.13G},{:.13G}\n".format(len_beam_axis_m, len_beam_axis_n)
|
||||
section_def = "*BEAM SECTION, {}{}{}\n".format(
|
||||
elsetdef,
|
||||
material,
|
||||
section_type
|
||||
)
|
||||
section_geo = f"{len_beam_axis_m:.13G},{len_beam_axis_n:.13G}\n"
|
||||
section_def = f"*BEAM SECTION, {elsetdef}{material}{section_type}\n"
|
||||
elif beamsec_obj.SectionType == "Circular":
|
||||
diameter = beamsec_obj.CircDiameter.getValueAs("mm").Value
|
||||
section_type = ", SECTION=CIRC"
|
||||
section_geo = "{:.13G}\n".format(diameter)
|
||||
section_def = "*BEAM SECTION, {}{}{}\n".format(
|
||||
elsetdef,
|
||||
material,
|
||||
section_type
|
||||
)
|
||||
section_geo = f"{diameter:.13G}\n"
|
||||
section_def = f"*BEAM SECTION, {elsetdef}{material}{section_type}\n"
|
||||
elif beamsec_obj.SectionType == "Pipe":
|
||||
radius = 0.5 * beamsec_obj.PipeDiameter.getValueAs("mm").Value
|
||||
thickness = beamsec_obj.PipeThickness.getValueAs("mm").Value
|
||||
section_type = ", SECTION=PIPE"
|
||||
section_geo = "{:.13G},{:.13G}\n".format(radius, thickness)
|
||||
section_def = "*BEAM SECTION, {}{}{}\n".format(
|
||||
elsetdef,
|
||||
material,
|
||||
section_type
|
||||
)
|
||||
section_geo = f"{radius:.13G},{thickness:.13G}\n"
|
||||
section_def = f"*BEAM SECTION, {elsetdef}{material}{section_type}\n"
|
||||
f.write(section_def)
|
||||
f.write(section_geo)
|
||||
f.write(section_nor)
|
||||
@@ -96,9 +82,7 @@ def write_femelement_geometry(f, ccxwriter):
|
||||
if (section_type == "PIPE INLET") or (section_type == "PIPE OUTLET"):
|
||||
section_type = "PIPE INOUT"
|
||||
section_def = "*FLUID SECTION, {}TYPE={}, {}\n".format(
|
||||
elsetdef,
|
||||
section_type,
|
||||
material
|
||||
elsetdef, section_type, material
|
||||
)
|
||||
section_geo = liquid_section_def(fluidsec_obj, section_type)
|
||||
"""
|
||||
@@ -114,15 +98,15 @@ def write_femelement_geometry(f, ccxwriter):
|
||||
elif "shellthickness_obj" in matgeoset: # shell mesh
|
||||
shellth_obj = matgeoset["shellthickness_obj"]
|
||||
if ccxwriter.solver_obj.ModelSpace == "3D":
|
||||
section_def = "*SHELL SECTION, {}{}\n".format(elsetdef, material)
|
||||
section_def = f"*SHELL SECTION, {elsetdef}{material}\n"
|
||||
else:
|
||||
section_def = "*SOLID SECTION, {}{}\n".format(elsetdef, material)
|
||||
section_def = f"*SOLID SECTION, {elsetdef}{material}\n"
|
||||
thickness = shellth_obj.Thickness.getValueAs("mm").Value
|
||||
section_geo = "{:.13G}\n".format(thickness)
|
||||
section_geo = f"{thickness:.13G}\n"
|
||||
f.write(section_def)
|
||||
f.write(section_geo)
|
||||
f.write(section_geo)
|
||||
else: # solid mesh
|
||||
section_def = "*SOLID SECTION, {}{}\n".format(elsetdef, material)
|
||||
section_def = f"*SOLID SECTION, {elsetdef}{material}\n"
|
||||
f.write(section_def)
|
||||
|
||||
|
||||
@@ -134,68 +118,63 @@ def liquid_section_def(obj, section_type):
|
||||
manning_radius = obj.ManningRadius.getValueAs("mm").Value
|
||||
manning_coefficient = obj.ManningCoefficient
|
||||
section_geo = "{:.13G},{:.13G},{:.13G}\n".format(
|
||||
manning_area,
|
||||
manning_radius,
|
||||
manning_coefficient
|
||||
manning_area, manning_radius, manning_coefficient
|
||||
)
|
||||
return section_geo
|
||||
elif section_type == "PIPE ENLARGEMENT":
|
||||
enlarge_area1 = obj.EnlargeArea1.getValueAs("mm^2").Value
|
||||
enlarge_area2 = obj.EnlargeArea2.getValueAs("mm^2").Value
|
||||
section_geo = "{:.13G},{:.13G}\n".format(enlarge_area1, enlarge_area2)
|
||||
section_geo = f"{enlarge_area1:.13G},{enlarge_area2:.13G}\n"
|
||||
return section_geo
|
||||
elif section_type == "PIPE CONTRACTION":
|
||||
contract_area1 = obj.ContractArea1.getValueAs("mm^2").Value
|
||||
contract_area2 = obj.ContractArea2.getValueAs("mm^2").Value
|
||||
section_geo = "{:.13G},{:.13G}\n".format(contract_area1, contract_area2)
|
||||
section_geo = f"{contract_area1:.13G},{contract_area2:.13G}\n"
|
||||
return section_geo
|
||||
elif section_type == "PIPE ENTRANCE":
|
||||
entrance_pipe_area = obj.EntrancePipeArea.getValueAs("mm^2").Value
|
||||
entrance_area = obj.EntranceArea.getValueAs("mm^2").Value
|
||||
section_geo = "{:.13G},{:.13G}\n".format(entrance_pipe_area, entrance_area)
|
||||
section_geo = f"{entrance_pipe_area:.13G},{entrance_area:.13G}\n"
|
||||
return section_geo
|
||||
elif section_type == "PIPE DIAPHRAGM":
|
||||
diaphragm_pipe_area = obj.DiaphragmPipeArea.getValueAs("mm^2").Value
|
||||
diaphragm_area = obj.DiaphragmArea.getValueAs("mm^2").Value
|
||||
section_geo = "{:.13G},{:.13G}\n".format(diaphragm_pipe_area, diaphragm_area)
|
||||
section_geo = f"{diaphragm_pipe_area:.13G},{diaphragm_area:.13G}\n"
|
||||
return section_geo
|
||||
elif section_type == "PIPE BEND":
|
||||
bend_pipe_area = obj.BendPipeArea.getValueAs("mm^2").Value
|
||||
bend_radius_diameter = obj.BendRadiusDiameter
|
||||
bend_angle = obj.BendAngle
|
||||
bend_loss_coefficient = obj.BendLossCoefficient
|
||||
section_geo = ("{:.13G},{:.13G},{:.13G},{:.13G}\n".format(
|
||||
bend_pipe_area,
|
||||
bend_radius_diameter,
|
||||
bend_angle,
|
||||
bend_loss_coefficient
|
||||
))
|
||||
section_geo = "{:.13G},{:.13G},{:.13G},{:.13G}\n".format(
|
||||
bend_pipe_area, bend_radius_diameter, bend_angle, bend_loss_coefficient
|
||||
)
|
||||
return section_geo
|
||||
elif section_type == "PIPE GATE VALVE":
|
||||
gatevalve_pipe_area = obj.GateValvePipeArea.getValueAs("mm^2").Value
|
||||
gatevalve_closing_coeff = obj.GateValveClosingCoeff
|
||||
section_geo = "{:.13G},{:.13G}\n".format(gatevalve_pipe_area, gatevalve_closing_coeff)
|
||||
section_geo = f"{gatevalve_pipe_area:.13G},{gatevalve_closing_coeff:.13G}\n"
|
||||
return section_geo
|
||||
elif section_type == "PIPE WHITE-COLEBROOK":
|
||||
colebrooke_area = obj.ColebrookeArea.getValueAs("mm^2").Value
|
||||
colebrooke_diameter = 2 * obj.ColebrookeRadius.getValueAs("mm")
|
||||
colebrooke_grain_diameter = obj.ColebrookeGrainDiameter.getValueAs("mm")
|
||||
colebrooke_form_factor = obj.ColebrookeFormFactor
|
||||
section_geo = ("{:.13G},{:.13G},{},{:.13G},{:.13G}\n".format(
|
||||
section_geo = "{:.13G},{:.13G},{},{:.13G},{:.13G}\n".format(
|
||||
colebrooke_area,
|
||||
colebrooke_diameter,
|
||||
"-1",
|
||||
colebrooke_grain_diameter,
|
||||
colebrooke_form_factor
|
||||
))
|
||||
colebrooke_form_factor,
|
||||
)
|
||||
return section_geo
|
||||
elif section_type == "LIQUID PUMP":
|
||||
section_geo = ""
|
||||
for i in range(len(obj.PumpFlowRate)):
|
||||
flow_rate = obj.PumpFlowRate[i]
|
||||
top = obj.PumpHeadLoss[i]
|
||||
section_geo = "{:.13G},{:.13G},\n".format(section_geo + flow_rate, top)
|
||||
section_geo = "{}\n".format(section_geo)
|
||||
section_geo = f"{section_geo + flow_rate:.13G},{top:.13G},\n"
|
||||
section_geo = f"{section_geo}\n"
|
||||
return section_geo
|
||||
else:
|
||||
return ""
|
||||
|
||||
@@ -77,7 +77,7 @@ def write_femelement_material(f, ccxwriter):
|
||||
SH = FreeCAD.Units.Quantity(mat_obj.Material["SpecificHeat"])
|
||||
# SvdW: Add factor to force units to results base units of t/mm/s/K
|
||||
# FIXME: why not get it directly in the units needed ?
|
||||
SH_in_JkgK = SH.getValueAs("J/kg/K").Value * 1e+06
|
||||
SH_in_JkgK = SH.getValueAs("J/kg/K").Value * 1e06
|
||||
if mat_obj.Category == "Solid":
|
||||
TEC = FreeCAD.Units.Quantity(mat_obj.Material["ThermalExpansionCoefficient"])
|
||||
TEC_in_mmK = TEC.getValueAs("mm/mm/K").Value
|
||||
@@ -86,26 +86,26 @@ def write_femelement_material(f, ccxwriter):
|
||||
DV_in_tmms = DV.getValueAs("t/mm/s").Value
|
||||
|
||||
# write material properties
|
||||
f.write("** FreeCAD material name: {}\n".format(mat_info_name))
|
||||
f.write("** {}\n".format(mat_label))
|
||||
f.write("*MATERIAL, NAME={}\n".format(mat_name))
|
||||
f.write(f"** FreeCAD material name: {mat_info_name}\n")
|
||||
f.write(f"** {mat_label}\n")
|
||||
f.write(f"*MATERIAL, NAME={mat_name}\n")
|
||||
if mat_obj.Category == "Solid":
|
||||
f.write("*ELASTIC\n")
|
||||
f.write("{:.13G},{:.13G}\n".format(YM_in_MPa, PR))
|
||||
f.write(f"{YM_in_MPa:.13G},{PR:.13G}\n")
|
||||
if is_density_needed() is True:
|
||||
f.write("*DENSITY\n")
|
||||
f.write("{:.13G}\n".format(density_in_tonne_per_mm3))
|
||||
f.write(f"{density_in_tonne_per_mm3:.13G}\n")
|
||||
if ccxwriter.analysis_type == "thermomech":
|
||||
if mat_obj.Category == "Solid":
|
||||
f.write("*CONDUCTIVITY\n")
|
||||
f.write("{:.13G}\n".format(TC_in_WmK))
|
||||
f.write(f"{TC_in_WmK:.13G}\n")
|
||||
f.write("*EXPANSION\n")
|
||||
f.write("{:.13G}\n".format(TEC_in_mmK))
|
||||
f.write(f"{TEC_in_mmK:.13G}\n")
|
||||
f.write("*SPECIFIC HEAT\n")
|
||||
f.write("{:.13G}\n".format(SH_in_JkgK))
|
||||
f.write(f"{SH_in_JkgK:.13G}\n")
|
||||
elif mat_obj.Category == "Fluid":
|
||||
f.write("*FLUID CONSTANTS\n")
|
||||
f.write("{:.13G},{:.13G}\n".format(SH_in_JkgK, DV_in_tmms))
|
||||
f.write(f"{SH_in_JkgK:.13G},{DV_in_tmms:.13G}\n")
|
||||
|
||||
# nonlinear material properties
|
||||
if ccxwriter.solver_obj.MaterialNonlinearity == "nonlinear":
|
||||
@@ -119,5 +119,5 @@ def write_femelement_material(f, ccxwriter):
|
||||
else:
|
||||
f.write("*PLASTIC, HARDENING=KINEMATIC\n")
|
||||
for yield_point in nl_mat_obj.YieldPoints:
|
||||
f.write("{}\n".format(yield_point))
|
||||
f.write(f"{yield_point}\n")
|
||||
f.write("\n")
|
||||
|
||||
@@ -34,20 +34,14 @@ def write_footer(f, ccxwriter):
|
||||
|
||||
f.write("\n{}\n".format(59 * "*"))
|
||||
f.write("** CalculiX Input file\n")
|
||||
f.write("** written by --> FreeCAD {}.{}.{}\n".format(
|
||||
ccxwriter.fc_ver[0],
|
||||
ccxwriter.fc_ver[1],
|
||||
ccxwriter.fc_ver[2]
|
||||
))
|
||||
f.write("** written on --> {}\n".format(
|
||||
time.ctime()
|
||||
))
|
||||
f.write("** file name --> {}\n".format(
|
||||
os.path.basename(ccxwriter.document.FileName)
|
||||
))
|
||||
f.write("** analysis name --> {}\n".format(
|
||||
ccxwriter.analysis.Name
|
||||
))
|
||||
f.write(
|
||||
"** written by --> FreeCAD {}.{}.{}\n".format(
|
||||
ccxwriter.fc_ver[0], ccxwriter.fc_ver[1], ccxwriter.fc_ver[2]
|
||||
)
|
||||
)
|
||||
f.write(f"** written on --> {time.ctime()}\n")
|
||||
f.write(f"** file name --> {os.path.basename(ccxwriter.document.FileName)}\n")
|
||||
f.write(f"** analysis name --> {ccxwriter.analysis.Name}\n")
|
||||
f.write("**\n")
|
||||
f.write("**\n")
|
||||
f.write(ccxwriter.units_information)
|
||||
|
||||
@@ -67,13 +67,13 @@ def write_mesh(ccxwriter):
|
||||
group_param,
|
||||
volVariant=vol_variant,
|
||||
faceVariant=face_variant,
|
||||
edgeVariant=edge_variant
|
||||
edgeVariant=edge_variant,
|
||||
)
|
||||
|
||||
inpfile = codecs.open(ccxwriter.file_name, "w", encoding="utf-8")
|
||||
inpfile.write("{}\n".format(59 * "*"))
|
||||
inpfile.write("** {}\n".format(write_name))
|
||||
inpfile.write("*INCLUDE,INPUT={}\n".format(file_name_split))
|
||||
inpfile.write(f"** {write_name}\n")
|
||||
inpfile.write(f"*INCLUDE,INPUT={file_name_split}\n")
|
||||
|
||||
else:
|
||||
ccxwriter.femmesh_file = ccxwriter.file_name
|
||||
@@ -83,7 +83,7 @@ def write_mesh(ccxwriter):
|
||||
group_param,
|
||||
volVariant=vol_variant,
|
||||
faceVariant=face_variant,
|
||||
edgeVariant=edge_variant
|
||||
edgeVariant=edge_variant,
|
||||
)
|
||||
|
||||
# reopen file with "append" to add all the rest
|
||||
|
||||
@@ -47,11 +47,8 @@ def write_step_equation(f, ccxwriter):
|
||||
)
|
||||
if ccxwriter.solver_obj.IterationsMaximum:
|
||||
if ccxwriter.analysis_type == "thermomech" or ccxwriter.analysis_type == "static":
|
||||
step += ", INC={}".format(ccxwriter.solver_obj.IterationsMaximum)
|
||||
elif (
|
||||
ccxwriter.analysis_type == "frequency"
|
||||
or ccxwriter.analysis_type == "buckling"
|
||||
):
|
||||
step += f", INC={ccxwriter.solver_obj.IterationsMaximum}"
|
||||
elif ccxwriter.analysis_type == "frequency" or ccxwriter.analysis_type == "buckling":
|
||||
# parameter is for thermomechanical analysis only, see ccx manual *STEP
|
||||
pass
|
||||
# write STEP line
|
||||
@@ -126,23 +123,27 @@ def write_step_equation(f, ccxwriter):
|
||||
# ANALYSIS parameter line
|
||||
analysis_parameter = ""
|
||||
if ccxwriter.analysis_type == "static" or ccxwriter.analysis_type == "check":
|
||||
if ccxwriter.solver_obj.IterationsUserDefinedIncrementations is True \
|
||||
or ccxwriter.solver_obj.IterationsUserDefinedTimeStepLength is True:
|
||||
if (
|
||||
ccxwriter.solver_obj.IterationsUserDefinedIncrementations is True
|
||||
or ccxwriter.solver_obj.IterationsUserDefinedTimeStepLength is True
|
||||
):
|
||||
analysis_parameter = "{},{},{},{}".format(
|
||||
ccxwriter.solver_obj.TimeInitialStep,
|
||||
ccxwriter.solver_obj.TimeEnd,
|
||||
ccxwriter.solver_obj.TimeMinimumStep,
|
||||
ccxwriter.solver_obj.TimeMaximumStep
|
||||
ccxwriter.solver_obj.TimeMaximumStep,
|
||||
)
|
||||
elif ccxwriter.analysis_type == "frequency":
|
||||
if ccxwriter.solver_obj.EigenmodeLowLimit == 0.0 \
|
||||
and ccxwriter.solver_obj.EigenmodeHighLimit == 0.0:
|
||||
analysis_parameter = "{}\n".format(ccxwriter.solver_obj.EigenmodesCount)
|
||||
if (
|
||||
ccxwriter.solver_obj.EigenmodeLowLimit == 0.0
|
||||
and ccxwriter.solver_obj.EigenmodeHighLimit == 0.0
|
||||
):
|
||||
analysis_parameter = f"{ccxwriter.solver_obj.EigenmodesCount}\n"
|
||||
else:
|
||||
analysis_parameter = "{},{},{}\n".format(
|
||||
ccxwriter.solver_obj.EigenmodesCount,
|
||||
ccxwriter.solver_obj.EigenmodeLowLimit,
|
||||
ccxwriter.solver_obj.EigenmodeHighLimit
|
||||
ccxwriter.solver_obj.EigenmodeHighLimit,
|
||||
)
|
||||
elif ccxwriter.analysis_type == "thermomech":
|
||||
# OvG: 1.0 increment, total time 1 for steady state will cut back automatically
|
||||
@@ -150,10 +151,10 @@ def write_step_equation(f, ccxwriter):
|
||||
ccxwriter.solver_obj.TimeInitialStep,
|
||||
ccxwriter.solver_obj.TimeEnd,
|
||||
ccxwriter.solver_obj.TimeMinimumStep,
|
||||
ccxwriter.solver_obj.TimeMaximumStep
|
||||
ccxwriter.solver_obj.TimeMaximumStep,
|
||||
)
|
||||
elif ccxwriter.analysis_type == "buckling":
|
||||
analysis_parameter = "{}\n".format(ccxwriter.solver_obj.BucklingFactors)
|
||||
analysis_parameter = f"{ccxwriter.solver_obj.BucklingFactors}\n"
|
||||
|
||||
# write analysis type line, analysis parameter line
|
||||
f.write(analysis_type + "\n")
|
||||
|
||||
@@ -94,18 +94,22 @@ def write_step_output(f, ccxwriter):
|
||||
or femobj["Object"].TranslationalModeY != "Free"
|
||||
or femobj["Object"].TranslationalModeZ != "Free"
|
||||
):
|
||||
f.write("*NODE PRINT, NSET={}_RefNode, TOTALS=ONLY\n".format(femobj["Object"].Name))
|
||||
f.write(
|
||||
"*NODE PRINT, NSET={}_RefNode, TOTALS=ONLY\n".format(femobj["Object"].Name)
|
||||
)
|
||||
f.write("RF\n")
|
||||
if (
|
||||
femobj["Object"].RotationalModeX != "Free"
|
||||
or femobj["Object"].RotationalModeY != "Free"
|
||||
or femobj["Object"].RotationalModeZ != "Free"
|
||||
):
|
||||
f.write("*NODE PRINT, NSET={}_RotNode, TOTALS=ONLY\n".format(femobj["Object"].Name))
|
||||
f.write(
|
||||
"*NODE PRINT, NSET={}_RotNode, TOTALS=ONLY\n".format(femobj["Object"].Name)
|
||||
)
|
||||
f.write("RF\n")
|
||||
if ccxwriter.member.cons_fixed or ccxwriter.member.cons_displacement:
|
||||
f.write("\n")
|
||||
f.write("*OUTPUT, FREQUENCY={}".format(ccxwriter.solver_obj.OutputFrequency))
|
||||
f.write(f"*OUTPUT, FREQUENCY={ccxwriter.solver_obj.OutputFrequency}")
|
||||
|
||||
# there is no need to write all integration point results
|
||||
# as long as there is no reader for them
|
||||
|
||||
@@ -108,22 +108,10 @@ units_information = """*********************************************************
|
||||
|
||||
class FemInputWriterCcx(writerbase.FemInputWriter):
|
||||
def __init__(
|
||||
self,
|
||||
analysis_obj,
|
||||
solver_obj,
|
||||
mesh_obj,
|
||||
member,
|
||||
dir_name=None,
|
||||
mat_geo_sets=None
|
||||
self, analysis_obj, solver_obj, mesh_obj, member, dir_name=None, mat_geo_sets=None
|
||||
):
|
||||
writerbase.FemInputWriter.__init__(
|
||||
self,
|
||||
analysis_obj,
|
||||
solver_obj,
|
||||
mesh_obj,
|
||||
member,
|
||||
dir_name,
|
||||
mat_geo_sets
|
||||
self, analysis_obj, solver_obj, mesh_obj, member, dir_name, mat_geo_sets
|
||||
)
|
||||
self.mesh_name = self.mesh_object.Name
|
||||
self.file_name = join(self.dir_name, self.mesh_name + ".inp")
|
||||
@@ -138,10 +126,7 @@ class FemInputWriterCcx(writerbase.FemInputWriter):
|
||||
time_start = time.process_time()
|
||||
FreeCAD.Console.PrintMessage("\n") # because of time print in separate line
|
||||
FreeCAD.Console.PrintMessage("CalculiX solver input writing...\n")
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Input file:{}\n"
|
||||
.format(self.file_name)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(f"Input file:{self.file_name}\n")
|
||||
|
||||
if self.solver_obj.SplitInputWriter is True:
|
||||
FreeCAD.Console.PrintMessage("Split input file.\n")
|
||||
@@ -161,7 +146,9 @@ class FemInputWriterCcx(writerbase.FemInputWriter):
|
||||
|
||||
# element sets constraints
|
||||
self.write_constraints_meshsets(inpfile, self.member.cons_centrif, con_centrif)
|
||||
self.write_constraints_meshsets(inpfile, self.member.cons_bodyheatsource, con_bodyheatsource)
|
||||
self.write_constraints_meshsets(
|
||||
inpfile, self.member.cons_bodyheatsource, con_bodyheatsource
|
||||
)
|
||||
|
||||
# node sets
|
||||
self.write_constraints_meshsets(inpfile, self.member.cons_fixed, con_fixed)
|
||||
@@ -193,12 +180,16 @@ class FemInputWriterCcx(writerbase.FemInputWriter):
|
||||
|
||||
# constraints dependent from steps
|
||||
self.write_constraints_propdata(inpfile, self.member.cons_fixed, con_fixed)
|
||||
self.write_constraints_propdata(inpfile, self.member.cons_rigidbody_step, con_rigidbody_step)
|
||||
self.write_constraints_propdata(
|
||||
inpfile, self.member.cons_rigidbody_step, con_rigidbody_step
|
||||
)
|
||||
self.write_constraints_propdata(inpfile, self.member.cons_displacement, con_displacement)
|
||||
self.write_constraints_propdata(inpfile, self.member.cons_sectionprint, con_sectionprint)
|
||||
self.write_constraints_propdata(inpfile, self.member.cons_selfweight, con_selfweight)
|
||||
self.write_constraints_propdata(inpfile, self.member.cons_centrif, con_centrif)
|
||||
self.write_constraints_propdata(inpfile, self.member.cons_bodyheatsource, con_bodyheatsource)
|
||||
self.write_constraints_propdata(
|
||||
inpfile, self.member.cons_bodyheatsource, con_bodyheatsource
|
||||
)
|
||||
self.write_constraints_meshsets(inpfile, self.member.cons_force, con_force)
|
||||
self.write_constraints_meshsets(inpfile, self.member.cons_pressure, con_pressure)
|
||||
self.write_constraints_propdata(inpfile, self.member.cons_temperature, con_temperature)
|
||||
@@ -216,17 +207,14 @@ class FemInputWriterCcx(writerbase.FemInputWriter):
|
||||
inpfile.close()
|
||||
|
||||
writetime = round((time.process_time() - time_start), 3)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Writing time CalculiX input file: {} seconds.\n".format(writetime)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(f"Writing time CalculiX input file: {writetime} seconds.\n")
|
||||
|
||||
# return
|
||||
if self.femelement_count_test is True:
|
||||
return self.file_name
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
"Problems on writing input file, check report prints.\n\n"
|
||||
)
|
||||
FreeCAD.Console.PrintError("Problems on writing input file, check report prints.\n\n")
|
||||
return ""
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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)")
|
||||
)
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -34,21 +34,19 @@ if FreeCAD.GuiUp:
|
||||
from pivy import coin
|
||||
|
||||
|
||||
class BaseProxy(object):
|
||||
class BaseProxy:
|
||||
|
||||
BaseType = "App::FeaturePython"
|
||||
|
||||
def __init__(self, obj):
|
||||
obj.Proxy = self
|
||||
obj.addProperty(
|
||||
"App::PropertyLinkSubList", "References",
|
||||
"Base", "")
|
||||
obj.addProperty("App::PropertyLinkSubList", "References", "Base", "")
|
||||
|
||||
def execute(self, obj):
|
||||
return True
|
||||
|
||||
|
||||
class BaseViewProxy(object):
|
||||
class BaseViewProxy:
|
||||
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
|
||||
@@ -38,11 +38,12 @@ except ImportError:
|
||||
raise Exception("No Fenics modules found, please install them.\n")
|
||||
|
||||
|
||||
class XDMFReader(object):
|
||||
class XDMFReader:
|
||||
"""
|
||||
Reads XDMF file and provides unified interface for returning
|
||||
cell functions or facet functions.
|
||||
"""
|
||||
|
||||
def __init__(self, xdmffilename):
|
||||
"""
|
||||
Sets filename and sets mesh instance to None.
|
||||
@@ -69,12 +70,7 @@ class XDMFReader(object):
|
||||
xdmffile.close()
|
||||
|
||||
def readCellExpression(
|
||||
self,
|
||||
group_value_dict,
|
||||
value_type="scalar",
|
||||
overlap=lambda x: x[0],
|
||||
*args,
|
||||
**kwargs
|
||||
self, group_value_dict, value_type="scalar", overlap=lambda x: x[0], *args, **kwargs
|
||||
):
|
||||
"""
|
||||
Reads cell expression and returns it.
|
||||
@@ -82,28 +78,19 @@ class XDMFReader(object):
|
||||
value_type_dictionary = {
|
||||
"scalar": ScalarCellExpressionFromXDMF,
|
||||
"vector2d": Vector2DCellExpressionFromXDMF,
|
||||
"vector3d": Vector3DCellExpressionFromXDMF}
|
||||
"vector3d": Vector3DCellExpressionFromXDMF,
|
||||
}
|
||||
|
||||
self.readMesh()
|
||||
xdmffile = fenics.XDMFFile(self.xdmffilename)
|
||||
cf = value_type_dictionary[value_type.lower()](
|
||||
group_value_dict,
|
||||
overlap=overlap,
|
||||
*args, **kwargs
|
||||
group_value_dict, overlap=overlap, *args, **kwargs
|
||||
)
|
||||
cf.init()
|
||||
for (key, value) in cf.group_value_dict.items():
|
||||
cf.markers[key] = fenics.MeshFunction(
|
||||
"size_t",
|
||||
self.mesh,
|
||||
self.mesh.topology().dim()
|
||||
)
|
||||
for key, value in cf.group_value_dict.items():
|
||||
cf.markers[key] = fenics.MeshFunction("size_t", self.mesh, self.mesh.topology().dim())
|
||||
xdmffile.read(cf.markers[key], key)
|
||||
cf.dx[key] = fenics.Measure(
|
||||
"dx",
|
||||
domain=self.mesh,
|
||||
subdomain_data=cf.markers[key]
|
||||
)
|
||||
cf.dx[key] = fenics.Measure("dx", domain=self.mesh, subdomain_data=cf.markers[key])
|
||||
xdmffile.close()
|
||||
return cf
|
||||
|
||||
@@ -115,34 +102,30 @@ class XDMFReader(object):
|
||||
xdmffile = fenics.XDMFFile(self.xdmffilename)
|
||||
ff = FacetFunctionFromXDMF(group_value_dict, *args, **kwargs)
|
||||
ff.init()
|
||||
for (key, value) in ff.group_value_dict.items():
|
||||
for key, value in ff.group_value_dict.items():
|
||||
ff.markers[key] = fenics.MeshFunction(
|
||||
"size_t",
|
||||
self.mesh,
|
||||
self.mesh.topology().dim() - 1
|
||||
"size_t", self.mesh, self.mesh.topology().dim() - 1
|
||||
)
|
||||
xdmffile.read(ff.markers[key], key)
|
||||
ff.marked[key] = value.get("marked", 1)
|
||||
ff.ds[key] = fenics.Measure(
|
||||
"ds",
|
||||
domain=self.mesh,
|
||||
subdomain_data=ff.markers[key]
|
||||
)
|
||||
ff.ds[key] = fenics.Measure("ds", domain=self.mesh, subdomain_data=ff.markers[key])
|
||||
ff.bcs[key] = value
|
||||
xdmffile.close()
|
||||
return ff
|
||||
|
||||
|
||||
class CellExpressionFromXDMF(object):
|
||||
class CellExpressionFromXDMF:
|
||||
"""
|
||||
Creates cell function expression from XDMF file.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, group_value_dict,
|
||||
default=lambda x: 0.,
|
||||
self,
|
||||
group_value_dict,
|
||||
default=lambda x: 0.0,
|
||||
check_marked=(lambda x: x == 1),
|
||||
overlap=lambda x: x[0],
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
self.init()
|
||||
self.group_value_dict = group_value_dict
|
||||
@@ -160,7 +143,8 @@ class CellExpressionFromXDMF(object):
|
||||
def eval_cell_backend(self, values, x, cell):
|
||||
|
||||
values_list = [
|
||||
func(x) for (key, func) in self.group_value_dict.items()
|
||||
func(x)
|
||||
for (key, func) in self.group_value_dict.items()
|
||||
if self.check_marked(self.markers[key][cell.index])
|
||||
]
|
||||
return_value = self.overlap(values_list)
|
||||
@@ -183,16 +167,13 @@ class ScalarCellExpressionFromXDMF(fenics.Expression, CellExpressionFromXDMF):
|
||||
def __init__(
|
||||
self,
|
||||
group_value_dict,
|
||||
default=lambda x: 0.,
|
||||
default=lambda x: 0.0,
|
||||
check_marked=(lambda x: x == 1),
|
||||
overlap=lambda x: x[0],
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
CellExpressionFromXDMF.__init__(
|
||||
self, group_value_dict,
|
||||
default=default,
|
||||
check_marked=check_marked,
|
||||
overlap=overlap
|
||||
self, group_value_dict, default=default, check_marked=check_marked, overlap=overlap
|
||||
)
|
||||
|
||||
def eval_cell(self, values, x, cell):
|
||||
@@ -209,13 +190,11 @@ class Vector3DCellExpressionFromXDMF(fenics.Expression, CellExpressionFromXDMF):
|
||||
group_value_dict,
|
||||
default=lambda x: np.zeros((3,)),
|
||||
check_marked=(lambda x: x == 1),
|
||||
overlap=lambda x: x[0], **kwargs
|
||||
overlap=lambda x: x[0],
|
||||
**kwargs,
|
||||
):
|
||||
CellExpressionFromXDMF.__init__(
|
||||
self, group_value_dict,
|
||||
default=default,
|
||||
check_marked=check_marked,
|
||||
overlap=overlap
|
||||
self, group_value_dict, default=default, check_marked=check_marked, overlap=overlap
|
||||
)
|
||||
|
||||
def eval_cell(self, values, x, cell):
|
||||
@@ -233,14 +212,10 @@ class Vector2DCellExpressionFromXDMF(fenics.Expression, CellExpressionFromXDMF):
|
||||
default=lambda x: np.zeros((2,)),
|
||||
check_marked=(lambda x: x == 1),
|
||||
overlap=lambda x: x[0],
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
CellExpressionFromXDMF.__init__(
|
||||
self,
|
||||
group_value_dict,
|
||||
default=default,
|
||||
check_marked=check_marked,
|
||||
overlap=overlap
|
||||
self, group_value_dict, default=default, check_marked=check_marked, overlap=overlap
|
||||
)
|
||||
|
||||
def eval_cell(self, values, x, cell):
|
||||
@@ -250,10 +225,11 @@ class Vector2DCellExpressionFromXDMF(fenics.Expression, CellExpressionFromXDMF):
|
||||
return (2,)
|
||||
|
||||
|
||||
class FacetFunctionFromXDMF(object):
|
||||
class FacetFunctionFromXDMF:
|
||||
"""
|
||||
Creates facet function from XDMF file.
|
||||
"""
|
||||
|
||||
def __init__(self, group_value_dict, *args, **kwargs):
|
||||
self.group_value_dict = group_value_dict
|
||||
self.init()
|
||||
@@ -266,18 +242,21 @@ class FacetFunctionFromXDMF(object):
|
||||
|
||||
def getDirichletBCs(self, vectorspace, *args, **kwargs):
|
||||
dbcs = []
|
||||
for (dict_key, dict_value) in self.bcs.items():
|
||||
for dict_key, dict_value in self.bcs.items():
|
||||
if dict_value["type"] == "Dirichlet":
|
||||
bc = fenics.DirichletBC(
|
||||
vectorspace,
|
||||
dict_value["value"],
|
||||
self.markers[dict_key],
|
||||
dict_value.get("marked", 1),
|
||||
*args, **kwargs
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
dbcs.append(bc)
|
||||
return dbcs
|
||||
|
||||
# TODO: write some functions to return integrals for Neumann and Robin
|
||||
# boundary conditions for the general case (i.e. vector, tensor)
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -41,22 +41,19 @@ def add_con_fixed(f, model, mystran_writer):
|
||||
spc_ids.append(conid)
|
||||
fixed_obj = femobj["Object"]
|
||||
# print(fixed_obj.Name)
|
||||
fixed_code += "# {}\n".format(fixed_obj.Name)
|
||||
fixed_code += f"# {fixed_obj.Name}\n"
|
||||
# node set
|
||||
fixed_code += "nodes_{} = {}\n".format(fixed_obj.Name, femobj["Nodes"])
|
||||
# all nodes in one line may be to long ... FIXME
|
||||
fixed_code += (
|
||||
"model.add_spc1(conid={}, components={}, nodes=nodes_{})\n\n"
|
||||
.format(conid, "123456", fixed_obj.Name)
|
||||
fixed_code += "model.add_spc1(conid={}, components={}, nodes=nodes_{})\n\n".format(
|
||||
conid, "123456", fixed_obj.Name
|
||||
)
|
||||
|
||||
# spcadd card
|
||||
spcadd_code = "# spcadd card, Single-Point Constraint Set Combination from SPC or SPC1 cards\n"
|
||||
spcadd_code += (
|
||||
"model.add_spcadd(conid=1, sets={})\n\n".format(spc_ids)
|
||||
)
|
||||
spcadd_code += f"model.add_spcadd(conid=1, sets={spc_ids})\n\n"
|
||||
|
||||
pynas_code = "{}\n{}".format(fixed_code, spcadd_code)
|
||||
pynas_code = f"{fixed_code}\n{spcadd_code}"
|
||||
# print(pynas_code)
|
||||
|
||||
# write the pyNastran code
|
||||
|
||||
@@ -44,28 +44,26 @@ def add_con_force(f, model, mystran_writer):
|
||||
force_obj = femobj["Object"]
|
||||
# print(force_obj.Name)
|
||||
|
||||
force_code += "# {}\n".format(force_obj.Name)
|
||||
force_code += f"# {force_obj.Name}\n"
|
||||
dirvec = femobj["Object"].DirectionVector
|
||||
print(femobj["NodeLoadTable"])
|
||||
for ref_shape in femobj["NodeLoadTable"]:
|
||||
force_code += "# {}\n".format(ref_shape[0])
|
||||
force_code += f"# {ref_shape[0]}\n"
|
||||
for n in sorted(ref_shape[1]):
|
||||
# the loads in ref_shape[1][n] are without unit
|
||||
node_load = ref_shape[1][n]
|
||||
force_code += (
|
||||
"model.add_force(sid={}, node={}, mag={}, xyz=({}, {}, {}))\n"
|
||||
.format(sid, n, node_load, dirvec.x, dirvec.y, dirvec.z)
|
||||
force_code += "model.add_force(sid={}, node={}, mag={}, xyz=({}, {}, {}))\n".format(
|
||||
sid, n, node_load, dirvec.x, dirvec.y, dirvec.z
|
||||
)
|
||||
force_code += "\n"
|
||||
|
||||
# generate calce factors lists
|
||||
# load card, static load combinations
|
||||
load_code = (
|
||||
"model.add_load(sid=1, scale=1.0, scale_factors={}, load_ids={})\n\n\n"
|
||||
.format(scale_factors, load_ids)
|
||||
load_code = "model.add_load(sid=1, scale=1.0, scale_factors={}, load_ids={})\n\n\n".format(
|
||||
scale_factors, load_ids
|
||||
)
|
||||
|
||||
pynas_code = "{}\n{}".format(force_code, load_code)
|
||||
pynas_code = f"{force_code}\n{load_code}"
|
||||
# print(pynas_code)
|
||||
|
||||
# write the pyNastran code
|
||||
|
||||
@@ -40,13 +40,9 @@ def add_femelement_geometry(f, model, mystran_writer):
|
||||
width = beamsec_obj.RectWidth.getValueAs("mm").Value
|
||||
pynas_code = "# pbarl card, properties of a simple beam element (CBAR entry)\n"
|
||||
pynas_code += "# defined by cross-sectional dimensions\n"
|
||||
pynas_code += (
|
||||
"dim = [{}, {}]\n"
|
||||
.format(width, height)
|
||||
)
|
||||
pynas_code += (
|
||||
"model.add_pbarl(pid=1, mid=1, Type={}, dim=dim, nsm=0.0)\n"
|
||||
.format('"BAR"')
|
||||
pynas_code += f"dim = [{width}, {height}]\n"
|
||||
pynas_code += "model.add_pbarl(pid=1, mid=1, Type={}, dim=dim, nsm=0.0)\n".format(
|
||||
'"BAR"'
|
||||
)
|
||||
pynas_code += "# pbarl.validate()\n\n\n"
|
||||
else:
|
||||
@@ -56,9 +52,8 @@ def add_femelement_geometry(f, model, mystran_writer):
|
||||
shellth_obj = mystran_writer.member.geos_shellthickness[0]["Object"]
|
||||
thickness = shellth_obj.Thickness.getValueAs("mm").Value
|
||||
pynas_code = "# pshell card, thin shell element properties\n"
|
||||
pynas_code += (
|
||||
"model.add_pshell(pid=1, mid1=1, t={}, mid2=1, mid3=1)\n\n\n"
|
||||
.format(thickness)
|
||||
pynas_code += "model.add_pshell(pid=1, mid1=1, t={}, mid2=1, mid3=1)\n\n\n".format(
|
||||
thickness
|
||||
)
|
||||
else:
|
||||
pynas_code = "# psolid card, defines solid element\n"
|
||||
|
||||
@@ -41,10 +41,7 @@ def add_femelement_material(f, model, mystran_writer):
|
||||
YM_in_MPa = YM.getValueAs("MPa").Value
|
||||
PR = float(mat_obj.Material["PoissonRatio"])
|
||||
pynas_code = "# mat1 card, material properties for linear isotropic material\n"
|
||||
pynas_code += (
|
||||
"mat = model.add_mat1(mid=1, E={:.1f}, G=None, nu={})\n\n\n"
|
||||
.format(YM_in_MPa, PR)
|
||||
)
|
||||
pynas_code += f"mat = model.add_mat1(mid=1, E={YM_in_MPa:.1f}, G=None, nu={PR})\n\n\n"
|
||||
|
||||
# write the pyNastran code
|
||||
f.write(pynas_code)
|
||||
|
||||
@@ -38,19 +38,14 @@ def add_mesh(f, model, mystran_writer):
|
||||
if not mystran_writer.femnodes_mesh:
|
||||
mystran_writer.femnodes_mesh = mystran_writer.femmesh.Nodes
|
||||
if not mystran_writer.femelement_table:
|
||||
mystran_writer.femelement_table = meshtools.get_femelement_table(
|
||||
mystran_writer.femmesh
|
||||
)
|
||||
mystran_writer.femelement_table = meshtools.get_femelement_table(mystran_writer.femmesh)
|
||||
mesh_eletype = exportNastranMesh.get_export_element_type(
|
||||
mystran_writer.femmesh,
|
||||
mystran_writer.femelement_table
|
||||
mystran_writer.femmesh, mystran_writer.femelement_table
|
||||
)
|
||||
|
||||
# get the pynas code
|
||||
mesh_pynas_code = exportNastranMesh.get_pynastran_mesh(
|
||||
mystran_writer.femnodes_mesh,
|
||||
mystran_writer.femelement_table,
|
||||
mesh_eletype
|
||||
mystran_writer.femnodes_mesh, mystran_writer.femelement_table, mesh_eletype
|
||||
)
|
||||
# print(mesh_pynas_code)
|
||||
|
||||
|
||||
@@ -45,18 +45,16 @@ ANALYSIS_TYPES = ["static"]
|
||||
|
||||
|
||||
def create(doc, name="SolverMystran"):
|
||||
return femutils.createObject(
|
||||
doc, name, Proxy, ViewProxy)
|
||||
return femutils.createObject(doc, name, Proxy, ViewProxy)
|
||||
|
||||
|
||||
class Proxy(solverbase.Proxy):
|
||||
"""The Fem::FemSolver's Proxy python type, add solver specific properties
|
||||
"""
|
||||
"""The Fem::FemSolver's Proxy python type, add solver specific properties"""
|
||||
|
||||
Type = "Fem::SolverMystran"
|
||||
|
||||
def __init__(self, obj):
|
||||
super(Proxy, self).__init__(obj)
|
||||
super().__init__(obj)
|
||||
obj.Proxy = self
|
||||
|
||||
# mystran_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Mystran")
|
||||
@@ -67,19 +65,21 @@ class Proxy(solverbase.Proxy):
|
||||
|
||||
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 editSupported(self):
|
||||
return True
|
||||
|
||||
def edit(self, directory):
|
||||
pattern = os.path.join(directory, "*.bdf") # TODO Mystran file ending
|
||||
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
|
||||
FreeCAD.Console.PrintMessage(f"{pattern}\n")
|
||||
f = glob.glob(pattern)[0]
|
||||
FemGui.open(f)
|
||||
# see comment in oofem solver file
|
||||
@@ -93,4 +93,5 @@ class ViewProxy(solverbase.ViewProxy):
|
||||
def getIcon(self):
|
||||
return ":/icons/FEM_SolverMystran.svg"
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -37,6 +37,7 @@ import FreeCAD
|
||||
|
||||
try:
|
||||
import hfcMystranNeuIn
|
||||
|
||||
result_reading = True
|
||||
except Exception:
|
||||
FreeCAD.Console.PrintWarning("Module to read results not found.\n")
|
||||
@@ -120,7 +121,7 @@ class Solve(run.Solve):
|
||||
args=[binary, infile], # pass empty param fails! [binary, "", infile]
|
||||
cwd=self.directory,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.signalAbort.add(self._process.terminate)
|
||||
self._process.communicate()
|
||||
@@ -132,8 +133,7 @@ class Solve(run.Solve):
|
||||
class Results(run.Results):
|
||||
|
||||
def run(self):
|
||||
prefs = FreeCAD.ParamGet(
|
||||
"User parameter:BaseApp/Preferences/Mod/Fem/General")
|
||||
prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General")
|
||||
if not prefs.GetBool("KeepResultsOnReRun", False):
|
||||
self.purge_results()
|
||||
if result_reading is True:
|
||||
@@ -160,10 +160,8 @@ class Results(run.Results):
|
||||
break
|
||||
else:
|
||||
# TODO: use solver framework status message system
|
||||
FreeCAD.Console.PrintError(
|
||||
"FEM: No results found at {}!\n"
|
||||
.format(neu_result_file)
|
||||
)
|
||||
FreeCAD.Console.PrintError(f"FEM: No results found at {neu_result_file}!\n")
|
||||
self.fail()
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -52,22 +52,10 @@ from .. import writerbase
|
||||
|
||||
class FemInputWriterMystran(writerbase.FemInputWriter):
|
||||
def __init__(
|
||||
self,
|
||||
analysis_obj,
|
||||
solver_obj,
|
||||
mesh_obj,
|
||||
member,
|
||||
dir_name=None,
|
||||
mat_geo_sets=None
|
||||
self, analysis_obj, solver_obj, mesh_obj, member, dir_name=None, mat_geo_sets=None
|
||||
):
|
||||
writerbase.FemInputWriter.__init__(
|
||||
self,
|
||||
analysis_obj,
|
||||
solver_obj,
|
||||
mesh_obj,
|
||||
member,
|
||||
dir_name,
|
||||
mat_geo_sets
|
||||
self, analysis_obj, solver_obj, mesh_obj, member, dir_name, mat_geo_sets
|
||||
)
|
||||
# basename (only for implementation purpose later delete this code
|
||||
# the mesh should never be None for Calculix solver
|
||||
@@ -78,17 +66,14 @@ class FemInputWriterMystran(writerbase.FemInputWriter):
|
||||
self.basename = "Mesh"
|
||||
self.solverinput_file = join(self.dir_name, self.basename + ".bdf")
|
||||
self.pynasinput_file = join(self.dir_name, self.basename + ".py")
|
||||
FreeCAD.Console.PrintLog(
|
||||
"FemInputWriterMystran --> self.dir_name --> {}\n"
|
||||
.format(self.dir_name)
|
||||
FreeCAD.Console.PrintLog(f"FemInputWriterMystran --> self.dir_name --> {self.dir_name}\n")
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"FemInputWriterMystra --> self.solverinput_file --> {}\n".format(
|
||||
self.solverinput_file
|
||||
)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"FemInputWriterMystra --> self.solverinput_file --> {}\n"
|
||||
.format(self.solverinput_file)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"FemInputWriterMystra --> self.pynasf_name --> {}\n"
|
||||
.format(self.pynasinput_file)
|
||||
f"FemInputWriterMystra --> self.pynasf_name --> {self.pynasinput_file}\n"
|
||||
)
|
||||
|
||||
def write_solver_input(self):
|
||||
@@ -112,8 +97,9 @@ class FemInputWriterMystran(writerbase.FemInputWriter):
|
||||
model = add_solver_control.add_solver_control(pynasf, model, self)
|
||||
|
||||
pynasf.write(
|
||||
"\n\nmodel.write_bdf('{}', enddata=True)\n"
|
||||
.format(join(self.dir_name, self.basename + "_pyNas.bdf"))
|
||||
"\n\nmodel.write_bdf('{}', enddata=True)\n".format(
|
||||
join(self.dir_name, self.basename + "_pyNas.bdf")
|
||||
)
|
||||
)
|
||||
|
||||
pynasf.close()
|
||||
@@ -121,9 +107,8 @@ class FemInputWriterMystran(writerbase.FemInputWriter):
|
||||
# print(model.get_bdf_stats())
|
||||
model.write_bdf(self.solverinput_file, enddata=True)
|
||||
|
||||
writing_time_string = (
|
||||
"Writing time input file: {} seconds"
|
||||
.format(round((time.process_time() - timestart), 2))
|
||||
writing_time_string = "Writing time input file: {} seconds".format(
|
||||
round((time.process_time() - timestart), 2)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(writing_time_string + " \n\n")
|
||||
|
||||
|
||||
@@ -46,10 +46,10 @@ def display(report, title=None, text=None):
|
||||
def displayGui(report, title=None, text=None):
|
||||
import FreeCADGui as Gui
|
||||
from . import reportdialog
|
||||
|
||||
if not report.isEmpty():
|
||||
mw = Gui.getMainWindow()
|
||||
dialog = reportdialog.ReportDialog(
|
||||
report, title, text, mw)
|
||||
dialog = reportdialog.ReportDialog(report, title, text, mw)
|
||||
dialog.exec_()
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ def displayLog(report):
|
||||
App.Console.PrintError("%s\n" % e)
|
||||
|
||||
|
||||
class Report(object):
|
||||
class Report:
|
||||
|
||||
def __init__(self):
|
||||
self.infos = []
|
||||
@@ -95,4 +95,5 @@ class Report(object):
|
||||
def error(self, msg):
|
||||
self.errors.append(msg)
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -42,7 +42,7 @@ INFO_COLOR = "Logging"
|
||||
class ReportDialog(QtGui.QDialog):
|
||||
|
||||
def __init__(self, report, title="Report", text=None, parent=None):
|
||||
super(ReportDialog, self).__init__(parent)
|
||||
super().__init__(parent)
|
||||
msgDetails = QtGui.QTextEdit()
|
||||
msgDetails.setReadOnly(True)
|
||||
msgDetails.setHtml(self._getText(report))
|
||||
@@ -73,8 +73,7 @@ class ReportDialog(QtGui.QDialog):
|
||||
return text
|
||||
|
||||
def _getColoredLine(self, text, outputwin_color_type):
|
||||
return '<font color="{}">{}</font>'.format(
|
||||
getOutputWinColor(outputwin_color_type), text
|
||||
)
|
||||
return f'<font color="{getOutputWinColor(outputwin_color_type)}">{text}</font>'
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -40,6 +40,7 @@ import os
|
||||
import os.path
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
# import threading # not used ATM
|
||||
|
||||
import FreeCAD as App
|
||||
@@ -69,7 +70,7 @@ _dirTypes = {}
|
||||
|
||||
|
||||
def run_fem_solver(solver, working_dir=None):
|
||||
""" Execute *solver* of the solver framework.
|
||||
"""Execute *solver* of the solver framework.
|
||||
|
||||
Uses :meth:`getMachine <femsolver.solverbase.Proxy.getMachine>` to obtain a
|
||||
:class:`Machine` instance of the solver. It than executes the Machine with
|
||||
@@ -101,6 +102,7 @@ def run_fem_solver(solver, working_dir=None):
|
||||
|
||||
if solver.Proxy.Type == "Fem::SolverCcxTools":
|
||||
from femtools.ccxtools import CcxTools as ccx
|
||||
|
||||
App.Console.PrintMessage("Run of CalxuliX ccx tools solver started.\n")
|
||||
fea = ccx(solver)
|
||||
fea.reset_mesh_purge_results_checked()
|
||||
@@ -117,7 +119,7 @@ def run_fem_solver(solver, working_dir=None):
|
||||
fea.ccx_run()
|
||||
fea.load_results()
|
||||
else:
|
||||
App.Console.PrintError("Houston, we have a problem...!\n{}\n".format(message))
|
||||
App.Console.PrintError(f"Houston, we have a problem...!\n{message}\n")
|
||||
App.Console.PrintMessage("Run of CalxuliX ccx tools solver finished.\n")
|
||||
else:
|
||||
# App.Console.PrintMessage("Frame work solver!\n")
|
||||
@@ -130,14 +132,12 @@ def run_fem_solver(solver, working_dir=None):
|
||||
error_message = (
|
||||
"Please save the file before executing the solver. "
|
||||
"This must be done because the location of the working "
|
||||
"directory is set to \"Beside *.FCStd File\"."
|
||||
'directory is set to "Beside *.FCStd File".'
|
||||
)
|
||||
App.Console.PrintError(error_message + "\n")
|
||||
if App.GuiUp:
|
||||
QtGui.QMessageBox.critical(
|
||||
FreeCADGui.getMainWindow(),
|
||||
"Can't start Solver",
|
||||
error_message
|
||||
FreeCADGui.getMainWindow(), "Can't start Solver", error_message
|
||||
)
|
||||
return
|
||||
except DirectoryDoesNotExistError:
|
||||
@@ -145,9 +145,7 @@ def run_fem_solver(solver, working_dir=None):
|
||||
App.Console.PrintError(error_message + "\n")
|
||||
if App.GuiUp:
|
||||
QtGui.QMessageBox.critical(
|
||||
FreeCADGui.getMainWindow(),
|
||||
"Can't start Solver",
|
||||
error_message
|
||||
FreeCADGui.getMainWindow(), "Can't start Solver", error_message
|
||||
)
|
||||
return
|
||||
if not machine.running:
|
||||
@@ -158,6 +156,7 @@ def run_fem_solver(solver, working_dir=None):
|
||||
if machine.failed is True:
|
||||
App.Console.PrintError("Machine failed to run.\n")
|
||||
from .report import displayLog
|
||||
|
||||
displayLog(machine.report)
|
||||
if App.GuiUp:
|
||||
error_message = (
|
||||
@@ -165,11 +164,12 @@ def run_fem_solver(solver, working_dir=None):
|
||||
"of the following errors are resolved."
|
||||
)
|
||||
from .report import display
|
||||
|
||||
display(machine.report, "Run Report", error_message)
|
||||
|
||||
|
||||
def getMachine(solver, path=None):
|
||||
""" Get or create :class:`Machine` using caching mechanism.
|
||||
"""Get or create :class:`Machine` using caching mechanism.
|
||||
|
||||
:param solver:
|
||||
A document object which must be a framework compliant solver. This means
|
||||
@@ -257,14 +257,12 @@ def _getBesideBase(solver):
|
||||
error_message = (
|
||||
"Please save the file before executing the solver. "
|
||||
"This must be done because the location of the working "
|
||||
"directory is set to \"Beside *.FCStd File\"."
|
||||
'directory is set to "Beside *.FCStd File".'
|
||||
)
|
||||
App.Console.PrintError(error_message + "\n")
|
||||
if App.GuiUp:
|
||||
QtGui.QMessageBox.critical(
|
||||
FreeCADGui.getMainWindow(),
|
||||
"Can't start Solver",
|
||||
error_message
|
||||
FreeCADGui.getMainWindow(), "Can't start Solver", error_message
|
||||
)
|
||||
raise MustSaveError()
|
||||
# TODO may be do not abort but use a temporary directory
|
||||
@@ -273,8 +271,7 @@ def _getBesideBase(solver):
|
||||
|
||||
def _getCustomDir(solver):
|
||||
base = _getCustomBase(solver)
|
||||
specificPath = os.path.join(
|
||||
base, solver.Document.Name, solver.Label)
|
||||
specificPath = os.path.join(base, solver.Document.Name, solver.Label)
|
||||
specificPath = _getUniquePath(specificPath)
|
||||
if not os.path.isdir(specificPath):
|
||||
os.makedirs(specificPath)
|
||||
@@ -288,9 +285,7 @@ def _getCustomBase(solver):
|
||||
App.Console.PrintError(error_message + "\n")
|
||||
if App.GuiUp:
|
||||
QtGui.QMessageBox.critical(
|
||||
FreeCADGui.getMainWindow(),
|
||||
"Can't start Solver",
|
||||
error_message
|
||||
FreeCADGui.getMainWindow(), "Can't start Solver", error_message
|
||||
)
|
||||
raise DirectoryDoesNotExistError("Invalid path")
|
||||
return path
|
||||
@@ -309,7 +304,7 @@ def _getUniquePath(path):
|
||||
class BaseTask(task.Thread):
|
||||
|
||||
def __init__(self):
|
||||
super(BaseTask, self).__init__()
|
||||
super().__init__()
|
||||
self.solver = None
|
||||
self.directory = None
|
||||
self.testmode = None
|
||||
@@ -321,10 +316,8 @@ class BaseTask(task.Thread):
|
||||
|
||||
class Machine(BaseTask):
|
||||
|
||||
def __init__(
|
||||
self, solver, directory, check,
|
||||
prepare, solve, results, testmode):
|
||||
super(Machine, self).__init__()
|
||||
def __init__(self, solver, directory, check, prepare, solve, results, testmode):
|
||||
super().__init__()
|
||||
self.solver = solver
|
||||
self.directory = directory
|
||||
self.signalState = set()
|
||||
@@ -346,11 +339,7 @@ class Machine(BaseTask):
|
||||
self._confTasks()
|
||||
self._isReset = False
|
||||
self._pendingState = self.state
|
||||
while (
|
||||
not self.aborted
|
||||
and not self.failed
|
||||
and self._pendingState <= self.target
|
||||
):
|
||||
while not self.aborted and not self.failed and self._pendingState <= self.target:
|
||||
task = self._getTask(self._pendingState)
|
||||
self._runTask(task)
|
||||
self.report.extend(task.report)
|
||||
@@ -363,21 +352,14 @@ class Machine(BaseTask):
|
||||
self._applyPending()
|
||||
|
||||
def reset(self, newState=CHECK):
|
||||
state = (self.state
|
||||
if self._pendingState is None
|
||||
else self._pendingState)
|
||||
state = self.state if self._pendingState is None else self._pendingState
|
||||
if newState < state:
|
||||
self._isReset = True
|
||||
self._state = newState
|
||||
signal.notify(self.signalState)
|
||||
|
||||
def _confTasks(self):
|
||||
tasks = [
|
||||
self.check,
|
||||
self.prepare,
|
||||
self.solve,
|
||||
self.results
|
||||
]
|
||||
tasks = [self.check, self.prepare, self.solve, self.results]
|
||||
for t in tasks:
|
||||
t.solver = self.solver
|
||||
t.directory = self.directory
|
||||
@@ -397,6 +379,7 @@ class Machine(BaseTask):
|
||||
|
||||
def killer():
|
||||
task.abort()
|
||||
|
||||
self.signalAbort.add(killer)
|
||||
task.signalStatus.add(statusProxy)
|
||||
task.start()
|
||||
@@ -428,10 +411,7 @@ class Check(BaseTask):
|
||||
self.fail()
|
||||
return False
|
||||
elif len(meshes) > 1:
|
||||
self.report.error(
|
||||
"Too many meshes. "
|
||||
"More than one mesh is not supported."
|
||||
)
|
||||
self.report.error("Too many meshes. More than one mesh is not supported.")
|
||||
self.fail()
|
||||
return False
|
||||
return True
|
||||
@@ -439,10 +419,7 @@ class Check(BaseTask):
|
||||
def check_material_exists(self):
|
||||
objs = self.get_several_member("App::MaterialObjectPython")
|
||||
if len(objs) == 0:
|
||||
self.report.error(
|
||||
"Missing a material object. "
|
||||
"At least one material is required."
|
||||
)
|
||||
self.report.error("Missing a material object. At least one material is required.")
|
||||
self.fail()
|
||||
return False
|
||||
return True
|
||||
@@ -507,10 +484,7 @@ class Check(BaseTask):
|
||||
if femutils.is_of_type(m, *sc):
|
||||
supported = True
|
||||
if not supported:
|
||||
self.report.warning(
|
||||
"Ignored unsupported constraint: {}"
|
||||
.format(m.Label)
|
||||
)
|
||||
self.report.warning(f"Ignored unsupported constraint: {m.Label}")
|
||||
return True
|
||||
|
||||
|
||||
@@ -538,7 +512,7 @@ class Results(BaseTask):
|
||||
pass
|
||||
|
||||
|
||||
class _DocObserver(object):
|
||||
class _DocObserver:
|
||||
|
||||
_instance = None
|
||||
_WHITELIST = [
|
||||
@@ -546,11 +520,7 @@ class _DocObserver(object):
|
||||
"App::MaterialObject",
|
||||
"Fem::FemMeshObject",
|
||||
]
|
||||
_BLACKLIST_PROPS = [
|
||||
"Label",
|
||||
"ElmerOutput",
|
||||
"ElmerResult"
|
||||
]
|
||||
_BLACKLIST_PROPS = ["Label", "ElmerOutput", "ElmerResult"]
|
||||
|
||||
def __init__(self):
|
||||
self._saved = {}
|
||||
|
||||
@@ -51,7 +51,7 @@ import FreeCAD
|
||||
|
||||
|
||||
class DirSetting:
|
||||
""" Enum of possible directory setting values.
|
||||
"""Enum of possible directory setting values.
|
||||
|
||||
Strings used to indicate the solver directory setting set in FreeCADs
|
||||
setting system. Returned by :func:`get_dir_setting` for that purpose. There
|
||||
@@ -71,6 +71,7 @@ class DirSetting:
|
||||
Use directory set below. Create own subdirectory for every solver. Name
|
||||
directory after the solver label prefixed with the document name.
|
||||
"""
|
||||
|
||||
TEMPORARY = "temporary"
|
||||
BESIDE = "beside"
|
||||
CUSTOM = "custom"
|
||||
@@ -82,7 +83,7 @@ _GENERAL_PARAM = _PARAM_PATH + "General"
|
||||
|
||||
|
||||
def get_binary(name, silent=False):
|
||||
""" Find binary of solver *name* honoring user settings.
|
||||
"""Find binary of solver *name* honoring user settings.
|
||||
|
||||
Return the specific path set by the user in FreeCADs settings/parameter
|
||||
system if set or the default binary name if no specific path is set. If no
|
||||
@@ -101,14 +102,13 @@ def get_binary(name, silent=False):
|
||||
if not silent:
|
||||
FreeCAD.Console.PrintError(
|
||||
"Settings solver name: {} not found in "
|
||||
"solver settings modules _SOLVER_PARAM dirctionary.\n"
|
||||
.format(name)
|
||||
"solver settings modules _SOLVER_PARAM dirctionary.\n".format(name)
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def get_cores(name):
|
||||
""" Read number of CPU cores for solver *name* honoring user settings.
|
||||
"""Read number of CPU cores for solver *name* honoring user settings.
|
||||
|
||||
Returns number of CPU cores to be used for the solver run
|
||||
|
||||
@@ -120,14 +120,13 @@ def get_cores(name):
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
"Settings solver name: {} not found in "
|
||||
"solver settings modules _SOLVER_PARAM dirctionary.\n"
|
||||
.format(name)
|
||||
"solver settings modules _SOLVER_PARAM dirctionary.\n".format(name)
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def get_write_comments(name):
|
||||
""" Check whether "write_comments" is set for solver.
|
||||
"""Check whether "write_comments" is set for solver.
|
||||
|
||||
Returns ``True`` if the "write_comments" setting/parameter is set for the
|
||||
solver with the id *name*. Returns ``False`` otherwise. If the solver is
|
||||
@@ -140,20 +139,19 @@ def get_write_comments(name):
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
"Settings solver name: {} not found in "
|
||||
"solver settings modules _SOLVER_PARAM dirctionary.\n"
|
||||
.format(name)
|
||||
"solver settings modules _SOLVER_PARAM dirctionary.\n".format(name)
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def get_custom_dir():
|
||||
""" Get value for :term:`General/CustomDirectoryPath` parameter. """
|
||||
"""Get value for :term:`General/CustomDirectoryPath` parameter."""
|
||||
param_group = FreeCAD.ParamGet(_GENERAL_PARAM)
|
||||
return param_group.GetString("CustomDirectoryPath")
|
||||
|
||||
|
||||
def get_dir_setting():
|
||||
""" Return directory setting set by the user.
|
||||
"""Return directory setting set by the user.
|
||||
|
||||
Return one of the three possible values of the :class:`DirSetting` enum
|
||||
depending on the setting set in FreeCAD parameter system. Result dependes
|
||||
@@ -169,8 +167,7 @@ def get_dir_setting():
|
||||
|
||||
|
||||
def get_default_solver():
|
||||
""" Return default solver name.
|
||||
"""
|
||||
"""Return default solver name."""
|
||||
solver_map = {0: "None"}
|
||||
if get_binary("Calculix", True):
|
||||
solver_map[1] = "CalculiXCcxTools"
|
||||
@@ -184,8 +181,8 @@ def get_default_solver():
|
||||
return solver_map[param_group.GetInt("DefaultSolver", 0)]
|
||||
|
||||
|
||||
class _SolverDlg(object):
|
||||
""" Internal query logic for solver specific settings.
|
||||
class _SolverDlg:
|
||||
"""Internal query logic for solver specific settings.
|
||||
|
||||
Each instance queries settings for one specific solver (e.g. Elmer) common
|
||||
among all solvers. To clarify: There are a few settings that are useful
|
||||
@@ -233,26 +230,26 @@ class _SolverDlg(object):
|
||||
# without any additional user input
|
||||
# see ccxttols, it works for Windows and Linux there
|
||||
binary = self.default
|
||||
FreeCAD.Console.PrintLog("Solver binary path default: {} \n".format(binary))
|
||||
FreeCAD.Console.PrintLog(f"Solver binary path default: {binary} \n")
|
||||
|
||||
# check if use_default is set to True
|
||||
# if True the standard binary path will be overwritten with a user binary path
|
||||
if self.param_group.GetBool(self.use_default, True) is False:
|
||||
binary = self.param_group.GetString(self.custom_path)
|
||||
FreeCAD.Console.PrintLog("Solver binary path user setting: {} \n".format(binary))
|
||||
FreeCAD.Console.PrintLog(f"Solver binary path user setting: {binary} \n")
|
||||
|
||||
# get the whole binary path name for the given command or binary path and return it
|
||||
# None is returned if the binary has not been found
|
||||
# The user does not know what exactly has going wrong.
|
||||
from shutil import which as find_bin
|
||||
|
||||
the_found_binary = find_bin(binary)
|
||||
if (the_found_binary is None) and (not silent):
|
||||
FreeCAD.Console.PrintError(
|
||||
"The binary has not been found. Full binary search path: {}\n"
|
||||
.format(binary)
|
||||
f"The binary has not been found. Full binary search path: {binary}\n"
|
||||
)
|
||||
else:
|
||||
FreeCAD.Console.PrintLog("Found solver binary path: {}\n".format(the_found_binary))
|
||||
FreeCAD.Console.PrintLog(f"Found solver binary path: {the_found_binary}\n")
|
||||
return the_found_binary
|
||||
|
||||
def get_cores(self):
|
||||
@@ -268,25 +265,30 @@ _SOLVER_PARAM = {
|
||||
default="ccx",
|
||||
param_path=_PARAM_PATH + "Ccx",
|
||||
use_default="UseStandardCcxLocation",
|
||||
custom_path="ccxBinaryPath"),
|
||||
custom_path="ccxBinaryPath",
|
||||
),
|
||||
"ElmerSolver": _SolverDlg(
|
||||
default="ElmerSolver",
|
||||
param_path=_PARAM_PATH + "Elmer",
|
||||
use_default="UseStandardElmerLocation",
|
||||
custom_path="elmerBinaryPath"),
|
||||
custom_path="elmerBinaryPath",
|
||||
),
|
||||
"ElmerGrid": _SolverDlg(
|
||||
default="ElmerGrid",
|
||||
param_path=_PARAM_PATH + "Elmer",
|
||||
use_default="UseStandardGridLocation",
|
||||
custom_path="gridBinaryPath"),
|
||||
custom_path="gridBinaryPath",
|
||||
),
|
||||
"Mystran": _SolverDlg(
|
||||
default="mystran",
|
||||
param_path=_PARAM_PATH + "Mystran",
|
||||
use_default="UseStandardMystranLocation",
|
||||
custom_path="mystranBinaryPath"),
|
||||
custom_path="mystranBinaryPath",
|
||||
),
|
||||
"Z88": _SolverDlg(
|
||||
default="z88r",
|
||||
param_path=_PARAM_PATH + "Z88",
|
||||
use_default="UseStandardZ88Location",
|
||||
custom_path="z88BinaryPath"),
|
||||
custom_path="z88BinaryPath",
|
||||
),
|
||||
}
|
||||
|
||||
@@ -33,4 +33,5 @@ def notify(signal, *args):
|
||||
for slot in signal:
|
||||
slot(*args)
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -39,10 +39,7 @@ import femsolver.run
|
||||
|
||||
_UPDATE_INTERVAL = 50
|
||||
_REPORT_TITLE = "Run Report"
|
||||
_REPORT_ERR = (
|
||||
"Failed to run. Please try again after all "
|
||||
"of the following errors are resolved."
|
||||
)
|
||||
_REPORT_ERR = "Failed to run. Please try again after all of the following errors are resolved."
|
||||
|
||||
|
||||
class ControlTaskPanel(QtCore.QObject):
|
||||
@@ -56,7 +53,7 @@ class ControlTaskPanel(QtCore.QObject):
|
||||
machineStateChanged = QtCore.Signal(float)
|
||||
|
||||
def __init__(self, machine):
|
||||
super(ControlTaskPanel, self).__init__()
|
||||
super().__init__()
|
||||
self.form = ControlWidget()
|
||||
self._machine = None
|
||||
|
||||
@@ -89,14 +86,12 @@ class ControlTaskPanel(QtCore.QObject):
|
||||
self.machineStatusChanged.connect(self.form.appendStatus)
|
||||
self.machineStatusCleared.connect(self.form.clearStatus)
|
||||
self.machineTimeChanged.connect(self.form.setTime)
|
||||
self.machineStateChanged.connect(
|
||||
lambda: self.form.updateState(self.machine))
|
||||
self.machineStateChanged.connect(lambda: self.form.updateState(self.machine))
|
||||
self.machineChanged.connect(self._updateTimer)
|
||||
|
||||
# Set initial machine. Signal updates the widget.
|
||||
self.machineChanged.connect(self.updateWidget)
|
||||
self.form.destroyed.connect(
|
||||
lambda: self.machineChanged.disconnect(self.updateWidget))
|
||||
self.form.destroyed.connect(lambda: self.machineChanged.disconnect(self.updateWidget))
|
||||
|
||||
self.machine = machine
|
||||
|
||||
@@ -125,8 +120,7 @@ class ControlTaskPanel(QtCore.QObject):
|
||||
@QtCore.Slot()
|
||||
def edit(self):
|
||||
self.machine.reset(femsolver.run.SOLVE)
|
||||
self.machine.solver.Proxy.edit(
|
||||
self.machine.directory)
|
||||
self.machine.solver.Proxy.edit(self.machine.directory)
|
||||
|
||||
@QtCore.Slot()
|
||||
def abort(self):
|
||||
@@ -142,8 +136,7 @@ class ControlTaskPanel(QtCore.QObject):
|
||||
@QtCore.Slot()
|
||||
def updateMachine(self):
|
||||
if self.form.directory() != self.machine.directory:
|
||||
self.machine = femsolver.run.getMachine(
|
||||
self.machine.solver, self.form.directory())
|
||||
self.machine = femsolver.run.getMachine(self.machine.solver, self.form.directory())
|
||||
|
||||
@QtCore.Slot()
|
||||
def _updateTimer(self):
|
||||
@@ -207,7 +200,7 @@ class ControlWidget(QtGui.QWidget):
|
||||
directoryChanged = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(ControlWidget, self).__init__(parent)
|
||||
super().__init__(parent)
|
||||
self._setupUi()
|
||||
self._inputFileName = ""
|
||||
|
||||
@@ -339,8 +332,8 @@ class ControlWidget(QtGui.QWidget):
|
||||
self._directoryGrp.setDisabled(False)
|
||||
self._writeBtt.setDisabled(False)
|
||||
self._editBtt.setDisabled(
|
||||
not machine.solver.Proxy.editSupported()
|
||||
or machine.state <= femsolver.run.PREPARE
|
||||
not machine.solver.Proxy.editSupported() or machine.state <= femsolver.run.PREPARE
|
||||
)
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -42,7 +42,7 @@ if App.GuiUp:
|
||||
from . import solver_taskpanel
|
||||
|
||||
|
||||
class Proxy(object):
|
||||
class Proxy:
|
||||
|
||||
BaseType = "Fem::FemSolverObjectPython"
|
||||
|
||||
@@ -60,8 +60,7 @@ class Proxy(object):
|
||||
raise NotImplementedError()
|
||||
|
||||
def addEquation(self, obj, eqId):
|
||||
obj.addObject(self.createEquation(
|
||||
obj.Document, eqId))
|
||||
obj.addObject(self.createEquation(obj.Document, eqId))
|
||||
|
||||
def editSupported(self):
|
||||
return False
|
||||
@@ -73,7 +72,7 @@ class Proxy(object):
|
||||
return True
|
||||
|
||||
|
||||
class ViewProxy(object):
|
||||
class ViewProxy:
|
||||
"""Proxy for FemSolverElmers View Provider."""
|
||||
|
||||
def __init__(self, vobj):
|
||||
@@ -87,23 +86,15 @@ class ViewProxy(object):
|
||||
error_message = (
|
||||
"Please save the file before opening the task panel. "
|
||||
"This must be done because the location of the working "
|
||||
"directory is set to \"Beside *.FCStd File\"."
|
||||
'directory is set to "Beside *.FCStd File".'
|
||||
)
|
||||
App.Console.PrintError(error_message + "\n")
|
||||
QtGui.QMessageBox.critical(
|
||||
Gui.getMainWindow(),
|
||||
"Can't open Task Panel",
|
||||
error_message
|
||||
)
|
||||
QtGui.QMessageBox.critical(Gui.getMainWindow(), "Can't open Task Panel", error_message)
|
||||
return False
|
||||
except DirectoryDoesNotExistError:
|
||||
error_message = "Selected working directory doesn't exist."
|
||||
App.Console.PrintError(error_message + "\n")
|
||||
QtGui.QMessageBox.critical(
|
||||
Gui.getMainWindow(),
|
||||
"Can't open Task Panel",
|
||||
error_message
|
||||
)
|
||||
QtGui.QMessageBox.critical(Gui.getMainWindow(), "Can't open Task Panel", error_message)
|
||||
return False
|
||||
task = solver_taskpanel.ControlTaskPanel(machine)
|
||||
Gui.Control.showDialog(task)
|
||||
@@ -121,4 +112,5 @@ class ViewProxy(object):
|
||||
def attach(self, vobj):
|
||||
pass
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -36,7 +36,7 @@ from . import report
|
||||
from . import signal
|
||||
|
||||
|
||||
class Task(object):
|
||||
class Task:
|
||||
|
||||
def __init__(self):
|
||||
self.report = None
|
||||
@@ -57,15 +57,13 @@ class Task(object):
|
||||
def stopping():
|
||||
self.stopTime = time.time()
|
||||
self.running = False
|
||||
|
||||
self.signalStopping.add(stopping)
|
||||
|
||||
@property
|
||||
def time(self):
|
||||
if self.startTime is not None:
|
||||
endTime = (
|
||||
self.stopTime
|
||||
if self.stopTime is not None
|
||||
else time.time())
|
||||
endTime = self.stopTime if self.stopTime is not None else time.time()
|
||||
return endTime - self.startTime
|
||||
return None
|
||||
|
||||
@@ -124,13 +122,12 @@ class Task(object):
|
||||
class Thread(Task):
|
||||
|
||||
def __init__(self):
|
||||
super(Thread, self).__init__()
|
||||
super().__init__()
|
||||
self._thread = None
|
||||
|
||||
def start(self):
|
||||
super(Thread, self).start()
|
||||
self._thread = threading.Thread(
|
||||
target=self.protector)
|
||||
super().start()
|
||||
self._thread = threading.Thread(target=self.protector)
|
||||
self._thread.daemon = True
|
||||
self._thread.start()
|
||||
self._attachObserver()
|
||||
@@ -144,8 +141,10 @@ class Thread(Task):
|
||||
self._thread.join()
|
||||
signal.notify(self.signalStopping)
|
||||
signal.notify(self.signalStopped)
|
||||
|
||||
thread = threading.Thread(target=waitForStop)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -36,15 +36,9 @@ import FreeCAD
|
||||
from femmesh import meshsetsgetter
|
||||
|
||||
|
||||
class FemInputWriter():
|
||||
class FemInputWriter:
|
||||
def __init__(
|
||||
self,
|
||||
analysis_obj,
|
||||
solver_obj,
|
||||
mesh_obj,
|
||||
member,
|
||||
dir_name=None,
|
||||
mat_geo_sets=None
|
||||
self, analysis_obj, solver_obj, mesh_obj, member, dir_name=None, mat_geo_sets=None
|
||||
):
|
||||
# class attributes from parameter values
|
||||
self.analysis = analysis_obj
|
||||
@@ -73,10 +67,10 @@ class FemInputWriter():
|
||||
make_tmp_dir = True
|
||||
if make_tmp_dir is True:
|
||||
from tempfile import mkdtemp
|
||||
|
||||
dir_name = mkdtemp(prefix="fcfem_")
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"The working directory '{}' was created and will be used."
|
||||
.format(dir_name)
|
||||
f"The working directory '{dir_name}' was created and will be used."
|
||||
)
|
||||
self.dir_name = dir_name
|
||||
|
||||
@@ -147,12 +141,7 @@ class FemInputWriter():
|
||||
# ********************************************************************************************
|
||||
# generic writer for constraints mesh sets and constraints property data
|
||||
# write constraint node sets, constraint face sets, constraint element sets
|
||||
def write_constraints_meshsets(
|
||||
self,
|
||||
f,
|
||||
femobjs,
|
||||
con_module
|
||||
):
|
||||
def write_constraints_meshsets(self, f, femobjs, con_module):
|
||||
if not femobjs:
|
||||
return
|
||||
|
||||
@@ -166,7 +155,7 @@ class FemInputWriter():
|
||||
for femobj in femobjs:
|
||||
# femobj --> dict, FreeCAD document object is femobj["Object"]
|
||||
the_obj = femobj["Object"]
|
||||
the_file.write("** {}\n".format(the_obj.Label))
|
||||
the_file.write(f"** {the_obj.Label}\n")
|
||||
con_module.write_meshdata_constraint(the_file, femobj, the_obj, self)
|
||||
if write_after != "":
|
||||
the_file.write(write_after)
|
||||
@@ -180,8 +169,8 @@ class FemInputWriter():
|
||||
f.write("** {}\n".format(write_name.replace("_", " ")))
|
||||
|
||||
if self.split_inpfile is True:
|
||||
file_name_split = "{}_{}.inp".format(self.mesh_name, write_name)
|
||||
f.write("*INCLUDE,INPUT={}\n".format(file_name_split))
|
||||
file_name_split = f"{self.mesh_name}_{write_name}.inp"
|
||||
f.write(f"*INCLUDE,INPUT={file_name_split}\n")
|
||||
inpfile_split = open(join(self.dir_name, file_name_split), "w")
|
||||
constraint_sets_loop_writing(inpfile_split, femobjs, write_before, write_after)
|
||||
inpfile_split.close()
|
||||
@@ -189,12 +178,7 @@ class FemInputWriter():
|
||||
constraint_sets_loop_writing(f, femobjs, write_before, write_after)
|
||||
|
||||
# write constraint property data
|
||||
def write_constraints_propdata(
|
||||
self,
|
||||
f,
|
||||
femobjs,
|
||||
con_module
|
||||
):
|
||||
def write_constraints_propdata(self, f, femobjs, con_module):
|
||||
|
||||
if not femobjs:
|
||||
return
|
||||
@@ -208,13 +192,13 @@ class FemInputWriter():
|
||||
|
||||
# write constraint to file
|
||||
f.write("\n{}\n".format(59 * "*"))
|
||||
f.write("** {}\n".format(con_module.get_constraint_title()))
|
||||
f.write(f"** {con_module.get_constraint_title()}\n")
|
||||
if write_before != "":
|
||||
f.write(write_before)
|
||||
for femobj in femobjs:
|
||||
# femobj --> dict, FreeCAD document object is femobj["Object"]
|
||||
the_obj = femobj["Object"]
|
||||
f.write("** {}\n".format(the_obj.Label))
|
||||
f.write(f"** {the_obj.Label}\n")
|
||||
con_module.write_constraint(f, femobj, the_obj, self)
|
||||
if write_after != "":
|
||||
f.write(write_after)
|
||||
|
||||
@@ -45,18 +45,16 @@ ANALYSIS_TYPES = ["static"]
|
||||
|
||||
|
||||
def create(doc, name="SolverZ88"):
|
||||
return femutils.createObject(
|
||||
doc, name, Proxy, ViewProxy)
|
||||
return femutils.createObject(doc, name, Proxy, ViewProxy)
|
||||
|
||||
|
||||
class Proxy(solverbase.Proxy):
|
||||
"""The Fem::FemSolver's Proxy python type, add solver specific properties
|
||||
"""
|
||||
"""The Fem::FemSolver's Proxy python type, add solver specific properties"""
|
||||
|
||||
Type = "Fem::SolverZ88"
|
||||
|
||||
def __init__(self, obj):
|
||||
super(Proxy, self).__init__(obj)
|
||||
super().__init__(obj)
|
||||
obj.Proxy = self
|
||||
|
||||
# z88_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Z88")
|
||||
@@ -67,39 +65,41 @@ class Proxy(solverbase.Proxy):
|
||||
|
||||
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 editSupported(self):
|
||||
return True
|
||||
|
||||
def edit(self, directory):
|
||||
pattern = os.path.join(directory, "z88i1.txt")
|
||||
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
|
||||
FreeCAD.Console.PrintMessage(f"{pattern}\n")
|
||||
f = glob.glob(pattern)[0]
|
||||
FemGui.open(f)
|
||||
pattern = os.path.join(directory, "z88i2.txt")
|
||||
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
|
||||
FreeCAD.Console.PrintMessage(f"{pattern}\n")
|
||||
f = glob.glob(pattern)[0]
|
||||
FemGui.open(f)
|
||||
pattern = os.path.join(directory, "z88i5.txt")
|
||||
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
|
||||
FreeCAD.Console.PrintMessage(f"{pattern}\n")
|
||||
f = glob.glob(pattern)[0]
|
||||
FemGui.open(f)
|
||||
pattern = os.path.join(directory, "z88man.txt")
|
||||
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
|
||||
FreeCAD.Console.PrintMessage(f"{pattern}\n")
|
||||
f = glob.glob(pattern)[0]
|
||||
FemGui.open(f)
|
||||
pattern = os.path.join(directory, "z88mat.txt")
|
||||
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
|
||||
FreeCAD.Console.PrintMessage(f"{pattern}\n")
|
||||
f = glob.glob(pattern)[0]
|
||||
FemGui.open(f)
|
||||
pattern = os.path.join(directory, "z88elp.txt")
|
||||
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
|
||||
FreeCAD.Console.PrintMessage(f"{pattern}\n")
|
||||
f = glob.glob(pattern)[0]
|
||||
FemGui.open(f)
|
||||
|
||||
@@ -112,4 +112,5 @@ class ViewProxy(solverbase.ViewProxy):
|
||||
def getIcon(self):
|
||||
return ":/icons/FEM_SolverZ88.svg"
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -76,11 +76,7 @@ class Prepare(run.Prepare):
|
||||
|
||||
# write solver input
|
||||
w = writer.FemInputWriterZ88(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
mesh_obj,
|
||||
meshdatagetter.member,
|
||||
self.directory
|
||||
self.analysis, self.solver, mesh_obj, meshdatagetter.member, self.directory
|
||||
)
|
||||
path = w.write_solver_input()
|
||||
# report to user if task succeeded
|
||||
@@ -111,7 +107,7 @@ class Solve(run.Solve):
|
||||
prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Z88")
|
||||
solver_index = prefs.GetInt("Solver", 0)
|
||||
solver_name = SOLVER_TYPES[solver_index]
|
||||
self.pushStatus("Used solver: {}\n".format(solver_name))
|
||||
self.pushStatus(f"Used solver: {solver_name}\n")
|
||||
|
||||
# run solver test mode
|
||||
# AFAIK: z88r needs to be run twice
|
||||
@@ -136,14 +132,14 @@ class Solve(run.Solve):
|
||||
cwd=self.directory,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
startupinfo=femutils.startProgramInfo(state)
|
||||
startupinfo=femutils.startProgramInfo(state),
|
||||
)
|
||||
else:
|
||||
self._process = subprocess.Popen(
|
||||
[binary, command, "-" + solver_name],
|
||||
cwd=self.directory,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
self.signalAbort.add(self._process.terminate)
|
||||
self._process.communicate()
|
||||
@@ -155,8 +151,7 @@ class Solve(run.Solve):
|
||||
class Results(run.Results):
|
||||
|
||||
def run(self):
|
||||
prefs = FreeCAD.ParamGet(
|
||||
"User parameter:BaseApp/Preferences/Mod/Fem/General")
|
||||
prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General")
|
||||
if not prefs.GetBool("KeepResultsOnReRun", False):
|
||||
self.purge_results()
|
||||
self.load_results()
|
||||
@@ -173,18 +168,14 @@ class Results(run.Results):
|
||||
def load_results(self):
|
||||
self.pushStatus("Import new results...\n")
|
||||
# displacements from z88o2 file
|
||||
disp_result_file = os.path.join(
|
||||
self.directory, "z88o2.txt")
|
||||
disp_result_file = os.path.join(self.directory, "z88o2.txt")
|
||||
if os.path.isfile(disp_result_file):
|
||||
result_name_prefix = "Z88_" + self.solver.AnalysisType + "_"
|
||||
importZ88O2Results.import_z88_disp(
|
||||
disp_result_file, self.analysis, result_name_prefix)
|
||||
importZ88O2Results.import_z88_disp(disp_result_file, self.analysis, result_name_prefix)
|
||||
else:
|
||||
# TODO: use solver framework status message system
|
||||
FreeCAD.Console.PrintError(
|
||||
"FEM: No results found at {}!\n"
|
||||
.format(disp_result_file)
|
||||
)
|
||||
FreeCAD.Console.PrintError(f"FEM: No results found at {disp_result_file}!\n")
|
||||
self.fail()
|
||||
|
||||
|
||||
## @}
|
||||
|
||||
@@ -39,21 +39,9 @@ from femmesh import meshtools
|
||||
|
||||
|
||||
class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
def __init__(
|
||||
self,
|
||||
analysis_obj,
|
||||
solver_obj,
|
||||
mesh_obj,
|
||||
member,
|
||||
dir_name=None
|
||||
):
|
||||
def __init__(self, analysis_obj, solver_obj, mesh_obj, member, dir_name=None):
|
||||
writerbase.FemInputWriter.__init__(
|
||||
self,
|
||||
analysis_obj,
|
||||
solver_obj,
|
||||
mesh_obj,
|
||||
member,
|
||||
dir_name
|
||||
self, analysis_obj, solver_obj, mesh_obj, member, dir_name
|
||||
)
|
||||
self.file_name = join(self.dir_name, "z88")
|
||||
|
||||
@@ -63,18 +51,11 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
timestart = time.process_time()
|
||||
FreeCAD.Console.PrintMessage("\n") # because of time print in separate line
|
||||
FreeCAD.Console.PrintMessage("Z88 solver input writing...\n")
|
||||
FreeCAD.Console.PrintLog(
|
||||
"FemInputWriterZ88 --> self.dir_name --> {}\n"
|
||||
.format(self.dir_name)
|
||||
)
|
||||
FreeCAD.Console.PrintLog(f"FemInputWriterZ88 --> self.dir_name --> {self.dir_name}\n")
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"FemInputWriterZ88 --> self.file_name --> {}\n"
|
||||
.format(self.file_name)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Write z88 input files to: {}\n"
|
||||
.format(self.dir_name)
|
||||
f"FemInputWriterZ88 --> self.file_name --> {self.file_name}\n"
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(f"Write z88 input files to: {self.dir_name}\n")
|
||||
control = self.set_z88_elparam()
|
||||
if control is False:
|
||||
return None
|
||||
@@ -86,12 +67,10 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
self.write_z88_integration_properties()
|
||||
self.write_z88_memory_parameter()
|
||||
self.write_z88_solver_parameter()
|
||||
writing_time_string = (
|
||||
"Writing time input file: {} seconds"
|
||||
.format(round((time.process_time() - timestart), 2))
|
||||
writing_time_string = "Writing time input file: {} seconds".format(
|
||||
round((time.process_time() - timestart), 2)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"{}\n\n".format(writing_time_string))
|
||||
FreeCAD.Console.PrintMessage(f"{writing_time_string}\n\n")
|
||||
return self.dir_name
|
||||
|
||||
# ********************************************************************************************
|
||||
@@ -107,14 +86,14 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
param = {4: z8804, 24: z8824, 23: z8823, 17: z8817, 16: z8816, 1: z8801, 10: z8810}
|
||||
# TODO: test elements 17, 16, 10, INTORD etc
|
||||
self.z88_element_type = importZ88Mesh.get_z88_element_type(
|
||||
self.femmesh,
|
||||
self.femelement_table
|
||||
self.femmesh, self.femelement_table
|
||||
)
|
||||
if self.z88_element_type in param:
|
||||
self.z88_elparam = param[self.z88_element_type]
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
"Element type not supported by Z88. Can not write Z88 solver input.\n")
|
||||
"Element type not supported by Z88. Can not write Z88 solver input.\n"
|
||||
)
|
||||
return False
|
||||
FreeCAD.Console.PrintMessage(self.z88_elparam)
|
||||
FreeCAD.Console.PrintMessage("\n")
|
||||
@@ -130,10 +109,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
mesh_file_path = self.file_name + "i1.txt"
|
||||
f = open(mesh_file_path, "w")
|
||||
importZ88Mesh.write_z88_mesh_to_file(
|
||||
self.femnodes_mesh,
|
||||
self.femelement_table,
|
||||
self.z88_element_type,
|
||||
f
|
||||
self.femnodes_mesh, self.femelement_table, self.z88_element_type, f
|
||||
)
|
||||
f.close()
|
||||
|
||||
@@ -145,9 +121,9 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
# write nodes to constraints_data (different from writing to file in ccxInpWriter
|
||||
for femobj in self.member.cons_fixed:
|
||||
for n in femobj["Nodes"]:
|
||||
constraints_data.append((n, "{} 1 2 0\n".format(n)))
|
||||
constraints_data.append((n, "{} 2 2 0\n".format(n)))
|
||||
constraints_data.append((n, "{} 3 2 0\n".format(n)))
|
||||
constraints_data.append((n, f"{n} 1 2 0\n"))
|
||||
constraints_data.append((n, f"{n} 2 2 0\n"))
|
||||
constraints_data.append((n, f"{n} 3 2 0\n"))
|
||||
|
||||
# forces constraints
|
||||
# write node loads to constraints_data
|
||||
@@ -159,15 +135,15 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
for n in sorted(ref_shape[1]):
|
||||
# the loads in ref_shape[1][n] are without unit
|
||||
node_load = ref_shape[1][n]
|
||||
if (direction_vec.x != 0.0):
|
||||
if direction_vec.x != 0.0:
|
||||
v1 = direction_vec.x * node_load
|
||||
constraints_data.append((n, "{} 1 1 {}\n".format(n, v1)))
|
||||
if (direction_vec.y != 0.0):
|
||||
constraints_data.append((n, f"{n} 1 1 {v1}\n"))
|
||||
if direction_vec.y != 0.0:
|
||||
v2 = direction_vec.y * node_load
|
||||
constraints_data.append((n, "{} 2 1 {}\n".format(n, v2)))
|
||||
if (direction_vec.z != 0.0):
|
||||
constraints_data.append((n, f"{n} 2 1 {v2}\n"))
|
||||
if direction_vec.z != 0.0:
|
||||
v3 = direction_vec.z * node_load
|
||||
constraints_data.append((n, "{} 3 1 {}\n".format(n, v3)))
|
||||
constraints_data.append((n, f"{n} 3 1 {v3}\n"))
|
||||
|
||||
# write constraints_data to file
|
||||
constraints_file_path = self.file_name + "i2.txt"
|
||||
@@ -193,7 +169,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
materials_file_path = self.file_name + "mat.txt"
|
||||
fms = open(materials_file_path, "w")
|
||||
fms.write("1\n")
|
||||
fms.write("1 {} {}".format(self.element_count, material_data_file_name))
|
||||
fms.write(f"1 {self.element_count} {material_data_file_name}")
|
||||
fms.write("\n")
|
||||
fms.close()
|
||||
material_data_file_path = join(self.dir_name, material_data_file_name)
|
||||
@@ -201,7 +177,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
YM = FreeCAD.Units.Quantity(mat_obj.Material["YoungsModulus"])
|
||||
YM_in_MPa = YM.getValueAs("MPa")
|
||||
PR = float(mat_obj.Material["PoissonRatio"])
|
||||
fmd.write("{0} {1:.3f}".format(YM_in_MPa, PR))
|
||||
fmd.write(f"{YM_in_MPa} {PR:.3f}")
|
||||
fmd.write("\n")
|
||||
fmd.close()
|
||||
|
||||
@@ -219,35 +195,24 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
elif beam_obj.SectionType == "Circular":
|
||||
diameter = beam_obj.CircDiameter.getValueAs("mm").Value
|
||||
from math import pi
|
||||
|
||||
area = 0.25 * pi * diameter * diameter
|
||||
else:
|
||||
FreeCAD.Console.PrintError(
|
||||
"Cross section type {} not supported, "
|
||||
"cross section area will be 0 in solver input.\n"
|
||||
.format(beam_obj.SectionType)
|
||||
"cross section area will be 0 in solver input.\n".format(beam_obj.SectionType)
|
||||
)
|
||||
# TODO make the check in prechecks and delete it here
|
||||
# no extensive errorhandling in writer
|
||||
# this way the solver will fail and an exception is raised somehow
|
||||
elements_data.append(
|
||||
"1 {} {} 0 0 0 0 0 0 "
|
||||
.format(self.element_count, area)
|
||||
)
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Be aware, only trusses are supported for edge meshes!\n"
|
||||
)
|
||||
elements_data.append(f"1 {self.element_count} {area} 0 0 0 0 0 0 ")
|
||||
FreeCAD.Console.PrintWarning("Be aware, only trusses are supported for edge meshes!\n")
|
||||
elif meshtools.is_face_femmesh(self.femmesh):
|
||||
thick_obj = self.member.geos_shellthickness[0]["Object"]
|
||||
thickness = thick_obj.Thickness.getValueAs("mm").Value
|
||||
elements_data.append(
|
||||
"1 {} {} 0 0 0 0 0 0 "
|
||||
.format(self.element_count, thickness)
|
||||
)
|
||||
elements_data.append(f"1 {self.element_count} {thickness} 0 0 0 0 0 0 ")
|
||||
elif meshtools.is_solid_femmesh(self.femmesh):
|
||||
elements_data.append(
|
||||
"1 {} 0 0 0 0 0 0 0"
|
||||
.format(self.element_count)
|
||||
)
|
||||
elements_data.append(f"1 {self.element_count} 0 0 0 0 0 0 0")
|
||||
else:
|
||||
FreeCAD.Console.PrintError("Error!\n")
|
||||
f = open(element_properties_file_path, "w")
|
||||
@@ -260,14 +225,14 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
# ********************************************************************************************
|
||||
def write_z88_integration_properties(self):
|
||||
integration_data = []
|
||||
integration_data.append("1 {} {} {}".format(
|
||||
self.element_count,
|
||||
self.z88_elparam["INTORD"],
|
||||
self.z88_elparam["INTOS"]
|
||||
))
|
||||
integration_data.append(
|
||||
"1 {} {} {}".format(
|
||||
self.element_count, self.z88_elparam["INTORD"], self.z88_elparam["INTOS"]
|
||||
)
|
||||
)
|
||||
integration_properties_file_path = self.file_name + "int.txt"
|
||||
f = open(integration_properties_file_path, "w")
|
||||
f.write("{}\n".format(len(integration_data)))
|
||||
f.write(f"{len(integration_data)}\n")
|
||||
for i in integration_data:
|
||||
f.write(i)
|
||||
f.write("\n")
|
||||
@@ -297,9 +262,9 @@ class FemInputWriterZ88(writerbase.FemInputWriter):
|
||||
output = ""
|
||||
for line in template_array:
|
||||
if line.find("MAXGS") > -1:
|
||||
line = " MAXGS {}".format(MaxGS)
|
||||
line = f" MAXGS {MaxGS}"
|
||||
if line.find("MAXKOI") > -1:
|
||||
line = " MAXKOI {}".format(MaxKOI)
|
||||
line = f" MAXKOI {MaxKOI}"
|
||||
output += line + "\n"
|
||||
|
||||
solver_parameter_file_path = self.file_name + ".dyn"
|
||||
|
||||
Reference in New Issue
Block a user