FEM: Add support for amplitudes with CalculiX (#22851)

This commit is contained in:
FEA-eng
2025-08-01 22:22:01 +02:00
committed by GitHub
parent c41c50e946
commit 79e6d8f016
41 changed files with 202 additions and 32 deletions

View File

@@ -148,6 +148,16 @@ ConstraintDisplacement::ConstraintDisplacement()
"ConstraintDisplacement",
App::Prop_None,
"Rotation in local Z direction");
ADD_PROPERTY_TYPE(EnableAmplitude,
(false),
"ConstraintDisplacement",
(App::PropertyType)(App::Prop_None),
"Amplitude of the displacement boundary condition");
ADD_PROPERTY_TYPE(AmplitudeValues,
(std::vector<std::string> {"0, 0", "1, 1"}),
"ConstraintFDisplacement",
(App::PropertyType)(App::Prop_None),
"Amplitude values");
}
App::DocumentObjectExecReturn* ConstraintDisplacement::execute()

View File

@@ -40,6 +40,9 @@ public:
/// Constructor
ConstraintDisplacement();
App::PropertyBool EnableAmplitude;
App::PropertyStringList AmplitudeValues;
// Displacement parameters
App::PropertyDistance xDisplacement;
App::PropertyDistance yDisplacement;

View File

@@ -54,6 +54,16 @@ ConstraintForce::ConstraintForce()
// by default use the null vector to indicate an invalid value
naturalDirectionVector = Base::Vector3d(0, 0, 0);
ADD_PROPERTY_TYPE(EnableAmplitude,
(false),
"ConstraintForce",
(App::PropertyType)(App::Prop_None),
"Amplitude of the force load");
ADD_PROPERTY_TYPE(AmplitudeValues,
(std::vector<std::string> {"0, 0", "1, 1"}),
"ConstraintForce",
(App::PropertyType)(App::Prop_None),
"Amplitude values");
}
App::DocumentObjectExecReturn* ConstraintForce::execute()

View File

@@ -38,6 +38,8 @@ public:
/// Constructor
ConstraintForce();
App::PropertyBool EnableAmplitude;
App::PropertyStringList AmplitudeValues;
App::PropertyForce Force;
App::PropertyLinkSub Direction;
App::PropertyBool Reversed;

View File

@@ -56,6 +56,16 @@ ConstraintHeatflux::ConstraintHeatflux()
App::Prop_None,
"Cavity radiation");
ADD_PROPERTY_TYPE(CavityName, ("cav"), "ConstraintHeatflux", App::Prop_None, "Cavity name");
ADD_PROPERTY_TYPE(EnableAmplitude,
(false),
"ConstraintHeatflux",
(App::PropertyType)(App::Prop_None),
"Amplitude of the heat flux load");
ADD_PROPERTY_TYPE(AmplitudeValues,
(std::vector<std::string> {"0, 0", "1, 1"}),
"ConstraintHeatflux",
(App::PropertyType)(App::Prop_None),
"Amplitude values");
}
App::DocumentObjectExecReturn* ConstraintHeatflux::execute()

View File

@@ -39,6 +39,9 @@ class FemExport ConstraintHeatflux: public Fem::Constraint
public:
ConstraintHeatflux();
App::PropertyBool EnableAmplitude;
App::PropertyStringList AmplitudeValues;
App::PropertyTemperature AmbientTemp;
/*App::PropertyFloat FaceTemp;*/
App::PropertyThermalTransferCoefficient FilmCoef;

View File

@@ -34,6 +34,16 @@ ConstraintPressure::ConstraintPressure()
{
ADD_PROPERTY(Pressure, (0.0));
ADD_PROPERTY(Reversed, (0));
ADD_PROPERTY_TYPE(EnableAmplitude,
(false),
"ConstraintPressure",
(App::PropertyType)(App::Prop_None),
"Amplitude of the pressure load");
ADD_PROPERTY_TYPE(AmplitudeValues,
(std::vector<std::string> {"0, 0", "1, 1"}),
"ConstraintPressure",
(App::PropertyType)(App::Prop_None),
"Amplitude values");
}
App::DocumentObjectExecReturn* ConstraintPressure::execute()

View File

@@ -37,6 +37,8 @@ class FemExport ConstraintPressure: public Fem::Constraint
public:
ConstraintPressure();
App::PropertyBool EnableAmplitude;
App::PropertyStringList AmplitudeValues;
App::PropertyPressure Pressure;
App::PropertyBool Reversed;

View File

@@ -44,6 +44,16 @@ ConstraintTemperature::ConstraintTemperature()
(App::PropertyType)(App::Prop_None),
"Type of constraint, temperature or concentrated heat flux");
ConstraintType.setEnums(ConstraintTypes);
ADD_PROPERTY_TYPE(EnableAmplitude,
(false),
"ConstraintTemperature",
(App::PropertyType)(App::Prop_None),
"Amplitude of the temperature boundary condition");
ADD_PROPERTY_TYPE(AmplitudeValues,
(std::vector<std::string> {"0, 0", "1, 1"}),
"ConstraintTemperature",
(App::PropertyType)(App::Prop_None),
"Amplitude values");
}
App::DocumentObjectExecReturn* ConstraintTemperature::execute()

View File

@@ -40,6 +40,9 @@ public:
/// Constructor
ConstraintTemperature();
App::PropertyBool EnableAmplitude;
App::PropertyStringList AmplitudeValues;
// Temperature parameters
App::PropertyTemperature Temperature;
App::PropertyPower CFlux;

View File

@@ -273,6 +273,7 @@ SET(FemSolverCalculix_SRCS
femsolver/calculix/write_femelement_geometry.py
femsolver/calculix/write_femelement_material.py
femsolver/calculix/write_femelement_matgeosets.py
femsolver/calculix/write_amplitude.py
femsolver/calculix/write_footer.py
femsolver/calculix/write_mesh.py
femsolver/calculix/write_step_equation.py

View File

@@ -78,7 +78,24 @@ class ConstraintBodyHeatSource(base_fempythonobject.BaseFemPythonObject):
value=["Dissipation Rate", "Total Power"],
)
)
prop.append(
_PropHelper(
type="App::PropertyBool",
name="EnableAmplitude",
group="Constraint Body Heat Source",
doc="Amplitude of the body heat source",
value=False,
)
)
prop.append(
_PropHelper(
type="App::PropertyStringList",
name="AmplitudeValues",
group="Constraint Body Heat Source",
doc="Amplitude values",
value=["0, 0", "1, 1"],
)
)
return prop
def onDocumentRestored(self, obj):

View File

@@ -0,0 +1,51 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * Copyright (c) 2025 Jakub Michalski <jakub.j.michalski[at]gmail.com> *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
__title__ = "FreeCAD FEM calculix amplitude"
__author__ = "Jakub Michalski"
__url__ = "https://www.freecad.org"
def write_amplitude(f, ccxwriter):
# write amplitude definitions for all analysis features that use them
def write_obj_amplitude(obj):
if obj.EnableAmplitude:
f.write(f"*AMPLITUDE, NAME={obj.Name}\n")
for value in obj.AmplitudeValues:
f.write(f"{value}\n")
f.write("\n")
constraint_lists = [
ccxwriter.member.cons_force,
ccxwriter.member.cons_pressure,
ccxwriter.member.cons_displacement,
ccxwriter.member.cons_heatflux,
ccxwriter.member.cons_temperature,
ccxwriter.member.cons_bodyheatsource,
]
for constraint_list in constraint_lists:
for entry in constraint_list:
write_obj_amplitude(entry["Object"])

View File

@@ -96,6 +96,9 @@ def write_constraint(f, femobj, bodyheatsource_obj, ccxwriter):
volume = ref_feat.getSubObject(ref_sub_obj).Volume
heat = bodyheatsource_obj.TotalPower / FreeCAD.Units.Quantity(volume, "mm^3")
# write to file
f.write("*DFLUX\n")
if bodyheatsource_obj.EnableAmplitude:
f.write(f"*DFLUX, AMPLITUDE={bodyheatsource_obj.Name}\n")
else:
f.write("*DFLUX\n")
f.write("{},BF,{:.13G}\n".format(bodyheatsource_obj.Name, heat.getValueAs("t/(mm*s^3)").Value))
f.write("\n")

View File

@@ -66,7 +66,10 @@ def write_constraint(f, femobj, disp_obj, ccxwriter):
# floats read from ccx should use {:.13G}, see comment in writer module
f.write("*BOUNDARY\n")
if disp_obj.EnableAmplitude:
f.write(f"*BOUNDARY, AMPLITUDE={disp_obj.Name}\n")
else:
f.write("*BOUNDARY\n")
if not disp_obj.xFree:
f.write(
"{},1,1,{}\n".format(

View File

@@ -35,7 +35,7 @@ def get_sets_name():
def get_before_write_meshdata_constraint():
return "*CLOAD\n"
return ""
def get_after_write_meshdata_constraint():
@@ -46,6 +46,10 @@ def write_meshdata_constraint(f, femobj, force_obj, ccxwriter):
# floats read from ccx should use {:.13G}, see comment in writer module
if force_obj.EnableAmplitude:
f.write(f"*CLOAD, AMPLITUDE={force_obj.Name}\n")
else:
f.write("*CLOAD\n")
direction_vec = femobj["Object"].DirectionVector
dir_zero_tol = 1e-15 # TODO: should this be more generally for more values?
# be careful with raising the tolerance, a big load would have an impact

View File

@@ -76,7 +76,12 @@ def write_meshdata_constraint(f, femobj, heatflux_obj, ccxwriter):
else:
return
f.write(f"*{heatflux_key_word}\n")
if heatflux_obj.EnableAmplitude:
heatflux_amplitude = f", AMPLITUDE={heatflux_obj.Name}"
else:
heatflux_amplitude = ""
f.write(f"*{heatflux_key_word}{heatflux_amplitude}\n")
for ref_shape in femobj["HeatFluxFaceTable"]:
elem_string = ref_shape[0]
face_table = ref_shape[1]

View File

@@ -48,12 +48,14 @@ def write_meshdata_constraint(f, femobj, prs_obj, ccxwriter):
# floats read from ccx should use {:.13G}, see comment in writer module
if prs_obj.EnableAmplitude:
f.write(f"*DLOAD, AMPLITUDE={prs_obj.Name}\n")
else:
f.write("*DLOAD\n")
rev = -1 if prs_obj.Reversed else 1
# the pressure has to be output in MPa
pressure_quantity = FreeCAD.Units.Quantity(prs_obj.Pressure.getValueAs("MPa"))
press_rev = rev * pressure_quantity
f.write("*DLOAD\n")
for ref_shape in femobj["PressureFaces"]:
# the loop is needed for compatibility reason
# in deprecated method get_pressure_obj_faces_depreciated

View File

@@ -68,8 +68,12 @@ def write_constraint(f, femobj, temp_obj, ccxwriter):
# floats read from ccx should use {:.13G}, see comment in writer module
NumberOfNodes = len(femobj["Nodes"])
if temp_obj.EnableAmplitude:
temp_amplitude = f", AMPLITUDE={temp_obj.Name}"
else:
temp_amplitude = ""
if temp_obj.ConstraintType == "Temperature":
f.write("*BOUNDARY\n")
f.write(f"*BOUNDARY{temp_amplitude}\n")
f.write(
"{},11,11,{}\n".format(
temp_obj.Name, FreeCAD.Units.Quantity(temp_obj.Temperature.getValueAs("K"))
@@ -77,7 +81,7 @@ def write_constraint(f, femobj, temp_obj, ccxwriter):
)
f.write("\n")
elif temp_obj.ConstraintType == "CFlux":
f.write("*CFLUX\n")
f.write(f"*CFLUX{temp_amplitude}\n")
# CFLUX has to be specified in mW
f.write(
"{},11,{}\n".format(

View File

@@ -60,6 +60,7 @@ from . import write_femelement_material
from . import write_femelement_matgeosets
from . import write_footer
from . import write_mesh
from . import write_amplitude
from . import write_step_equation
from . import write_step_output
from .. import writerbase
@@ -182,6 +183,9 @@ class FemInputWriterCcx(writerbase.FemInputWriter):
self.write_constraints_propdata(inpfile, self.member.cons_transform, con_transform)
self.write_constraints_propdata(inpfile, self.member.cons_rigidbody, con_rigidbody)
# amplitudes
write_amplitude.write_amplitude(inpfile, self)
# step equation
write_step_equation.write_step_equation(inpfile, self)

View File

@@ -507,8 +507,8 @@ FemConstraintFixed,3
***********************************************************
** constraints force node loads
*CLOAD
** FemConstraintForce
*CLOAD
** node loads on shape: Box:Face6
2,3,-0
4,3,-0

View File

@@ -750,8 +750,7 @@ Evolumes
** At least one step is needed to run an CalculiX analysis of FreeCAD
*STEP
*BUCKLE
10
10,0.01
***********************************************************
** Fixed Constraints
@@ -764,8 +763,8 @@ ConstraintFixed,3
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: Beam:Face6
65,3,-0.328125
66,3,-0.65625

View File

@@ -83,8 +83,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node load on shape: CantileverLine:Vertex2
2,3,-9000000

View File

@@ -83,8 +83,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node load on shape: CantileverLine:Vertex2
2,3,-9000000

View File

@@ -83,8 +83,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node load on shape: CantileverLine:Vertex2
2,3,-9000000

View File

@@ -403,8 +403,8 @@ ConstraintFixed,3
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: Box:Face2
5,3,187500
6,3,187500

View File

@@ -107,8 +107,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: CanileverPlate:Edge3
3,3,-2250000
4,3,-2250000

View File

@@ -95,8 +95,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: CanileverPlate:Edge3
3,3,-750000
4,3,-750000

View File

@@ -225,8 +225,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node load on shape: CantileverLine:Vertex2
2,3,-9000000

View File

@@ -83,8 +83,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node load on shape: CantileverLine:Vertex2
2,3,-9000000

View File

@@ -1583,8 +1583,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: CanileverPlate:Edge3
3,3,-642857.1428571
4,3,-642857.1428571

View File

@@ -313,8 +313,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: CanileverPlate:Edge3
3,3,-750000
4,3,-750000

View File

@@ -377,8 +377,8 @@ ConstraintFixed,3
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: Box:Face2
1,3,-0
2,3,-0

View File

@@ -377,8 +377,8 @@ ConstraintFixed,3
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node load on shape: Box:Vertex5
4,3,-2250000

View File

@@ -38394,8 +38394,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node load on shape: Load_place_point:Vertex1
5,2,-5000

View File

@@ -18630,8 +18630,8 @@ ConstraintFixed,3
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: BooleanFragments:Edge2
2,2,1666.666666667
8,2,1666.666666667

View File

@@ -10998,8 +10998,8 @@ ConstraintFixed,3
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: Cut:Face1
3,2,-0
4,2,-0

View File

@@ -27652,8 +27652,8 @@ ConstraintFixed,3
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: Box1:Face6
17,3,-0
19,3,-0

View File

@@ -2569,8 +2569,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce
*CLOAD
** node loads on shape: Face1:Edge4
9,2,-33.33333333333
11,2,-33.33333333333

View File

@@ -2581,8 +2581,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce1
*CLOAD
** node loads on shape: SquareTube:Edge9
1,1,2777.777777778
236,1,2777.777777778
@@ -2600,6 +2600,7 @@ ConstraintFixed,6
** ConstraintForce2
*CLOAD
** node loads on shape: SquareTube:Edge3
2,1,-2777.777777778
129,1,-2777.777777778
@@ -2617,6 +2618,7 @@ ConstraintFixed,6
** ConstraintForce3
*CLOAD
** node loads on shape: SquareTube:Edge11
1,2,2777.777777778
2,2,2777.777777778
@@ -2634,6 +2636,7 @@ ConstraintFixed,6
** ConstraintForce4
*CLOAD
** node loads on shape: SquareTube:Edge6
129,2,-2777.777777778
236,2,-2777.777777778

View File

@@ -2581,8 +2581,8 @@ ConstraintFixed,6
***********************************************************
** constraints force node loads
*CLOAD
** ConstraintForce1
*CLOAD
** node load on shape: Forces:Vertex1
1,1,-2777.78
@@ -2591,6 +2591,7 @@ ConstraintFixed,6
** ConstraintForce2
*CLOAD
** node load on shape: Forces:Vertex2
2,1,2777.78
@@ -2599,6 +2600,7 @@ ConstraintFixed,6
** ConstraintForce3
*CLOAD
** node load on shape: Forces:Vertex20
343,1,-5555.556
@@ -2616,6 +2618,7 @@ ConstraintFixed,6
** ConstraintForce4
*CLOAD
** node load on shape: Forces:Vertex9
131,1,5555.556
@@ -2633,6 +2636,7 @@ ConstraintFixed,6
** ConstraintForce5
*CLOAD
** node load on shape: Forces:Vertex43
1376,1,-11111.11166667
@@ -2653,6 +2657,7 @@ ConstraintFixed,6
** ConstraintForce6
*CLOAD
** node load on shape: Forces:Vertex31
758,1,11111.11166667
@@ -2673,6 +2678,7 @@ ConstraintFixed,6
** ConstraintForce7
*CLOAD
** node load on shape: Forces:Vertex1
1,2,-2777.78
@@ -2681,6 +2687,7 @@ ConstraintFixed,6
** ConstraintForce8
*CLOAD
** node load on shape: Forces:Vertex8
129,2,2777.78
@@ -2689,6 +2696,7 @@ ConstraintFixed,6
** ConstraintForce9
*CLOAD
** node load on shape: Forces:Vertex3
5,2,-5555.556
@@ -2706,6 +2714,7 @@ ConstraintFixed,6
** ConstraintForce10
*CLOAD
** node load on shape: Forces:Vertex15
238,2,5555.556
@@ -2723,6 +2732,7 @@ ConstraintFixed,6
** ConstraintForce11
*CLOAD
** node load on shape: Forces:Vertex25
429,2,-11111.11166667
@@ -2743,6 +2753,7 @@ ConstraintFixed,6
** ConstraintForce12
*CLOAD
** node load on shape: Forces:Vertex37
1067,2,11111.11166667