diff --git a/src/Mod/Fem/App/FemConstraintForce.cpp b/src/Mod/Fem/App/FemConstraintForce.cpp index 68099c550a..1a54f680aa 100644 --- a/src/Mod/Fem/App/FemConstraintForce.cpp +++ b/src/Mod/Fem/App/FemConstraintForce.cpp @@ -42,6 +42,9 @@ ConstraintForce::ConstraintForce() "ConstraintForce", (App::PropertyType)(App::Prop_None), "Element giving direction of constraint"); + // RefDispl must get a global scope, see + Direction.setScope(App::LinkScope::Global); + ADD_PROPERTY(Reversed, (0)); ADD_PROPERTY_TYPE(Points, (Base::Vector3d()), @@ -53,8 +56,9 @@ ConstraintForce::ConstraintForce() "ConstraintForce", App::PropertyType(App::Prop_ReadOnly | App::Prop_Output), "Direction of arrows"); - naturalDirectionVector = - Base::Vector3d(0, 0, 0); // by default use the null vector to indicate an invalid value + + // by default use the null vector to indicate an invalid value + naturalDirectionVector = Base::Vector3d(0, 0, 0); Points.setValues(std::vector()); } @@ -63,6 +67,21 @@ App::DocumentObjectExecReturn* ConstraintForce::execute() return Constraint::execute(); } +void ConstraintForce::handleChangedPropertyType(Base::XMLReader& reader, + const char* TypeName, + App::Property* prop) +{ + // property Force had App::PropertyFloat, was changed to App::PropertyForce + if (prop == &Force && strcmp(TypeName, "App::PropertyFloat") == 0) { + App::PropertyFloat ForceProperty; + // restore the PropertyFloat to be able to set its value + ForceProperty.Restore(reader); + // force uses m while FreeCAD uses internally mm thus + // e.g. "2.5" must become 2500 to result in 2.5 N + Force.setValue(ForceProperty.getValue() * 1000); + } +} + void ConstraintForce::onChanged(const App::Property* prop) { // Note: If we call this at the end, then the arrows are not oriented correctly initially diff --git a/src/Mod/Fem/App/FemConstraintForce.h b/src/Mod/Fem/App/FemConstraintForce.h index 8b35686f18..f669f63a6a 100644 --- a/src/Mod/Fem/App/FemConstraintForce.h +++ b/src/Mod/Fem/App/FemConstraintForce.h @@ -38,7 +38,7 @@ public: /// Constructor ConstraintForce(); - App::PropertyFloat Force; + App::PropertyForce Force; App::PropertyLinkSub Direction; App::PropertyBool Reversed; // Read-only (calculated values). These trigger changes in the ViewProvider @@ -55,6 +55,9 @@ public: } protected: + void handleChangedPropertyType(Base::XMLReader& reader, + const char* TypeName, + App::Property* prop) override; void onChanged(const App::Property* prop) override; private: diff --git a/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp b/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp index da3a74d901..324fc7800d 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp +++ b/src/Mod/Fem/Gui/TaskFemConstraintForce.cpp @@ -57,35 +57,12 @@ TaskFemConstraintForce::TaskFemConstraintForce(ViewProviderFemConstraintForce* C ui->setupUi(proxy); QMetaObject::connectSlotsByName(this); - // create a context menu for the listview of the references - createDeleteAction(ui->listReferences); - connect(deleteAction, &QAction::triggered, this, &TaskFemConstraintForce::onReferenceDeleted); - connect(ui->spinForce, - qOverload(&Gui::QuantitySpinBox::valueChanged), - this, - &TaskFemConstraintForce::onForceChanged); - connect(ui->buttonDirection, - &QToolButton::clicked, - this, - &TaskFemConstraintForce::onButtonDirection); - connect(ui->checkReverse, &QCheckBox::toggled, this, &TaskFemConstraintForce::onCheckReverse); - connect(ui->listReferences, - &QListWidget::itemClicked, - this, - &TaskFemConstraintForce::setSelection); - this->groupLayout()->addWidget(proxy); - // Temporarily prevent unnecessary feature recomputes - ui->spinForce->blockSignals(true); - ui->listReferences->blockSignals(true); - ui->buttonDirection->blockSignals(true); - ui->checkReverse->blockSignals(true); - // Get the feature data Fem::ConstraintForce* pcConstraint = static_cast(ConstraintView->getObject()); - double f = pcConstraint->Force.getValue(); + auto force = pcConstraint->Force.getQuantityValue(); std::vector Objects = pcConstraint->References.getValues(); std::vector SubElements = pcConstraint->References.getSubValues(); std::vector dirStrings = pcConstraint->Direction.getSubValues(); @@ -96,9 +73,10 @@ TaskFemConstraintForce::TaskFemConstraintForce(ViewProviderFemConstraintForce* C bool reversed = pcConstraint->Reversed.getValue(); // Fill data into dialog elements + ui->spinForce->setUnit(pcConstraint->Force.getUnit()); ui->spinForce->setMinimum(0); ui->spinForce->setMaximum(FLOAT_MAX); - ui->spinForce->setValue(f); + ui->spinForce->setValue(force); ui->listReferences->clear(); for (std::size_t i = 0; i < Objects.size(); i++) { ui->listReferences->addItem(makeRefText(Objects[i], SubElements[i])); @@ -109,15 +87,25 @@ TaskFemConstraintForce::TaskFemConstraintForce(ViewProviderFemConstraintForce* C ui->lineDirection->setText(dir.isEmpty() ? QString() : dir); ui->checkReverse->setChecked(reversed); - ui->spinForce->blockSignals(false); - ui->listReferences->blockSignals(false); - ui->buttonDirection->blockSignals(false); - ui->checkReverse->blockSignals(false); + // create a context menu for the listview of the references + createDeleteAction(ui->listReferences); + connect(deleteAction, &QAction::triggered, this, &TaskFemConstraintForce::onReferenceDeleted); + connect(ui->buttonDirection, + &QToolButton::clicked, + this, + &TaskFemConstraintForce::onButtonDirection); + connect(ui->checkReverse, &QCheckBox::toggled, this, &TaskFemConstraintForce::onCheckReverse); + connect(ui->listReferences, + &QListWidget::itemClicked, + this, + &TaskFemConstraintForce::setSelection); // Selection buttons buttonGroup->addButton(ui->btnAdd, (int)SelectionChangeModes::refAdd); buttonGroup->addButton(ui->btnRemove, (int)SelectionChangeModes::refRemove); + ui->spinForce->bind(pcConstraint->Force); + updateUI(); } @@ -259,13 +247,6 @@ void TaskFemConstraintForce::removeFromSelection() updateUI(); } -void TaskFemConstraintForce::onForceChanged(double f) -{ - Fem::ConstraintForce* pcConstraint = - static_cast(ConstraintView->getObject()); - pcConstraint->Force.setValue(f); -} - void TaskFemConstraintForce::onReferenceDeleted() { TaskFemConstraintForce::removeFromSelection(); // OvG: On right-click face is automatically @@ -359,9 +340,9 @@ void TaskFemConstraintForce::onCheckReverse(const bool pressed) pcConstraint->Reversed.setValue(pressed); } -double TaskFemConstraintForce::getForce() const +const std::string TaskFemConstraintForce::getForce() const { - return ui->spinForce->value().getValue(); + return ui->spinForce->value().getSafeUserString().toStdString(); } const std::string TaskFemConstraintForce::getReferences() const @@ -467,21 +448,10 @@ bool TaskDlgFemConstraintForce::accept() static_cast(parameter); try { - // Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "FEM force constraint changed")); - - if (parameterForce->getForce() <= 0) { - QMessageBox::warning(parameter, - tr("Input error"), - tr("Please specify a force greater than 0")); - return false; - } - else { - QByteArray num = QByteArray::number(parameterForce->getForce()); - Gui::Command::doCommand(Gui::Command::Doc, - "App.ActiveDocument.%s.Force = %s", - name.c_str(), - num.data()); - } + Gui::Command::doCommand(Gui::Command::Doc, + "App.ActiveDocument.%s.Force = \"%s\"", + name.c_str(), + parameterForce->getForce().c_str()); std::string dirname = parameterForce->getDirectionName().data(); std::string dirobj = parameterForce->getDirectionObject().data(); diff --git a/src/Mod/Fem/Gui/TaskFemConstraintForce.h b/src/Mod/Fem/Gui/TaskFemConstraintForce.h index 1791766e3a..2563d2de6e 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintForce.h +++ b/src/Mod/Fem/Gui/TaskFemConstraintForce.h @@ -55,7 +55,7 @@ public: explicit TaskFemConstraintForce(ViewProviderFemConstraintForce* ConstraintView, QWidget* parent = nullptr); ~TaskFemConstraintForce() override; - double getForce() const; + const std::string getForce() const; const std::string getReferences() const override; const std::string getDirectionName() const; const std::string getDirectionObject() const; @@ -63,7 +63,6 @@ public: private Q_SLOTS: void onReferenceDeleted(); - void onForceChanged(double); void onButtonDirection(const bool pressed = false); void onCheckReverse(bool); void addToSelection() override; diff --git a/src/Mod/Fem/Gui/TaskFemConstraintForce.ui b/src/Mod/Fem/Gui/TaskFemConstraintForce.ui index c8a4795cd8..595305221c 100644 --- a/src/Mod/Fem/Gui/TaskFemConstraintForce.ui +++ b/src/Mod/Fem/Gui/TaskFemConstraintForce.ui @@ -90,12 +90,18 @@ - Load [N] + Force + + N + + + 0.000000000000000 + 500.000000000000000 diff --git a/src/Mod/Fem/femmesh/meshtools.py b/src/Mod/Fem/femmesh/meshtools.py index 6876291268..77349a9d65 100644 --- a/src/Mod/Fem/femmesh/meshtools.py +++ b/src/Mod/Fem/femmesh/meshtools.py @@ -803,7 +803,8 @@ def get_force_obj_vertex_nodeload_table( # ("refshape_name.elemname", node_load_table) # ] force_obj_node_load_table = [] - node_load = frc_obj.Force / len(frc_obj.References) + force_quantity = FreeCAD.Units.Quantity(frc_obj.Force.getValueAs("N")) + node_load = force_quantity / len(frc_obj.References) for o, elem_tup in frc_obj.References: node_count = len(elem_tup) for elem in elem_tup: @@ -863,7 +864,8 @@ def get_force_obj_edge_nodeload_table( ) sum_ref_edge_length += ref_edge.Length if sum_ref_edge_length != 0: - force_per_sum_ref_edge_length = frc_obj.Force / sum_ref_edge_length + force_quantity = FreeCAD.Units.Quantity(frc_obj.Force.getValueAs("N")) + force_per_sum_ref_edge_length = force_quantity / sum_ref_edge_length for o, elem_tup in frc_obj.References: for elem in elem_tup: ref_edge = o.Shape.getElement(elem) @@ -907,7 +909,8 @@ def get_force_obj_edge_nodeload_table( for node in ref_shape[1]: sum_node_load += ref_shape[1][node] # for debugging - ratio = sum_node_load / frc_obj.Force + force_quantity = FreeCAD.Units.Quantity(frc_obj.Force.getValueAs("N")) + ratio = sum_node_load / force_quantity if ratio < 0.99 or ratio > 1.01: FreeCAD.Console.PrintMessage( "Deviation sum_node_load to frc_obj.Force is more than 1% : {}\n" @@ -927,7 +930,7 @@ def get_force_obj_edge_nodeload_table( ) FreeCAD.Console.PrintMessage( " frc_obj.Force: {}\n" - .format(frc_obj.Force) + .format(force_quantity) ) FreeCAD.Console.PrintMessage( " the reason could be simply a circle length --> " @@ -1135,7 +1138,8 @@ def get_force_obj_face_nodeload_table( ) sum_ref_face_area += ref_face.Area if sum_ref_face_area != 0: - force_per_sum_ref_face_area = frc_obj.Force / sum_ref_face_area + force_quantity = FreeCAD.Units.Quantity(frc_obj.Force.getValueAs("N")) + force_per_sum_ref_face_area = force_quantity / sum_ref_face_area for o, elem_tup in frc_obj.References: for elem in elem_tup: ref_face = o.Shape.getElement(elem) @@ -1178,7 +1182,8 @@ def get_force_obj_face_nodeload_table( for node in ref_shape[1]: sum_node_load += ref_shape[1][node] # for debugging - ratio = sum_node_load / frc_obj.Force + force_quantity = FreeCAD.Units.Quantity(frc_obj.Force.getValueAs("N")) + ratio = sum_node_load / force_quantity if ratio < 0.99 or ratio > 1.01: FreeCAD.Console.PrintMessage( "Deviation sum_node_load to frc_obj.Force is more than 1% : {}\n" @@ -1198,7 +1203,7 @@ def get_force_obj_face_nodeload_table( ) FreeCAD.Console.PrintMessage( " frc_obj.Force: {}\n" - .format(frc_obj.Force) + .format(force_quantity) ) FreeCAD.Console.PrintMessage( " the reason could be simply a circle area --> " diff --git a/src/Mod/Fem/femsolver/calculix/write_constraint_force.py b/src/Mod/Fem/femsolver/calculix/write_constraint_force.py index b12567e80c..a5a08b7945 100644 --- a/src/Mod/Fem/femsolver/calculix/write_constraint_force.py +++ b/src/Mod/Fem/femsolver/calculix/write_constraint_force.py @@ -54,14 +54,15 @@ def write_meshdata_constraint(f, femobj, force_obj, ccxwriter): f.write("** {}\n".format(ref_shape[0])) 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 = "{:.13E}".format(direction_vec.x * node_load) + v1 = "{}".format(direction_vec.x * node_load) f.write("{},1,{}\n".format(n, v1)) if abs(direction_vec.y) > dir_zero_tol: - v2 = "{:.13E}".format(direction_vec.y * node_load) + v2 = "{}".format(direction_vec.y * node_load) f.write("{},2,{}\n".format(n, v2)) if abs(direction_vec.z) > dir_zero_tol: - v3 = "{:.13E}".format(direction_vec.z * node_load) + v3 = "{}".format(direction_vec.z * node_load) f.write("{},3,{}\n".format(n, v3)) f.write("\n") f.write("\n") diff --git a/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py b/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py index 295a9a9a38..080976218d 100644 --- a/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py +++ b/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py @@ -97,7 +97,7 @@ class DeformationWriter: for obj in self.write.getMember("Fem::ConstraintForce"): if obj.References: for name in obj.References[0][1]: - force = self.write.getFromUi(obj.Force, "N", "M*L*T^-2") + force = float(obj.Force.getValueAs("N")) self.write.boundary(name, "Force 1", obj.DirectionVector.x * force) self.write.boundary(name, "Force 2", obj.DirectionVector.y * force) self.write.boundary(name, "Force 3", obj.DirectionVector.z * force) diff --git a/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py b/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py index 60c5327504..bc71a67368 100644 --- a/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py +++ b/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py @@ -313,7 +313,7 @@ class ElasticityWriter: for obj in self.write.getMember("Fem::ConstraintForce"): if obj.References: for name in obj.References[0][1]: - force = self.write.getFromUi(obj.Force, "N", "M*L*T^-2") + force = float(obj.Force.getValueAs("N")) self.write.boundary(name, "Force 1", obj.DirectionVector.x * force) self.write.boundary(name, "Force 2", obj.DirectionVector.y * force) self.write.boundary(name, "Force 3", obj.DirectionVector.z * force) diff --git a/src/Mod/Fem/femsolver/mystran/add_con_force.py b/src/Mod/Fem/femsolver/mystran/add_con_force.py index a267779c2a..4adc42f667 100644 --- a/src/Mod/Fem/femsolver/mystran/add_con_force.py +++ b/src/Mod/Fem/femsolver/mystran/add_con_force.py @@ -50,6 +50,7 @@ def add_con_force(f, model, mystran_writer): for ref_shape in femobj["NodeLoadTable"]: force_code += "# {}\n".format(ref_shape[0]) 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" diff --git a/src/Mod/Fem/femsolver/z88/writer.py b/src/Mod/Fem/femsolver/z88/writer.py index 55ca7b26bc..3bb63eb22f 100644 --- a/src/Mod/Fem/femsolver/z88/writer.py +++ b/src/Mod/Fem/femsolver/z88/writer.py @@ -157,6 +157,7 @@ class FemInputWriterZ88(writerbase.FemInputWriter): direction_vec = femobj["Object"].DirectionVector for ref_shape in femobj["NodeLoadTable"]: 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): v1 = direction_vec.x * node_load