Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Changes since last release
- Add an alternative wing airfoil parametrization that allows the specification of NACA4 codes directly in CPACS (with the trailing edge thickness). This makes a preprocessing step of writing sampled NACA profiles as point lists obsolete and improves the overall surface quality by internally generating B-Splines with a fine-tuned number of control points. [#1293](https://github.com/DLR-SC/tigl/pull/1293)
- The function `app.openFile` in the TiGLCreator scripting engine now accepts a configuration uid. [#1309](https://github.com/DLR-SC/tigl/pull/1309)
- Implemented the UpperLower wire in CTiglWingProfileNACA ([#1366](https://github.com/DLR-SC/tigl/issues/1366))
- Fix several broken fuselage structure Python bindings: `CCPACSFuselageStructure.get_skin()` (and other `boost::optional`-typed structure members) returned an unusable, un-registered object, and `CTiglStringerFrameBorderedObject` (base class of `CCPACSSkinSegment` and `CCPACSDoorAssemblyPosition`) was entirely excluded from the SWIG config, hiding `get_geometry()` on skin segments and cargo doors ([#1037](https://github.com/DLR-SC/tigl/issues/1037)).

- Fixes
- TiGLCreator: Fix incoherent selection between the 3D viewer and the CPACS tree for components without a dedicated editor (e.g. engines and pylons). Clicking such a component in the viewer now selects it in the tree (it was registered in the shape manager by uid only, so the click could not be resolved and the tree selection was cleared instead), and selecting it in the tree now highlights its geometry in the viewer. Also fix a related crash when opening the "Display Options" tab after such a failed selection: on deselection, a pointer to a stack-temporary `CPACSTreeItem` was emitted and cached, and later dereferenced after the temporary was gone (use-after-return). The placeholder emit was replaced by `nullptr` and stale items are no longer cached. Additionally, "Reset Options" no longer throws an uncaught exception for uids without a geometric component. Selecting a component (by click in the viewer or in the tree) now consistently highlights the whole component, i.e. the original and - if displayed - the mirrored part, with an immediate viewer update. Clicks in the 3D viewer now use the platform drag threshold to distinguish clicks from rubber-band selection, so a click with a slightly jittering mouse no longer silently clears the selection instead of selecting the clicked object ([#1419](https://github.com/DLR-SC/tigl/issues/1419))
Expand Down Expand Up @@ -114,6 +115,7 @@ Version 3.4.1
- Deprecation warning: The use of the node cpacsVersion right within the CPACS path /cpacs/header/ is deprecated according to CPACS 3.5. Hence, now a deprecation warning is printed if it is still used ([#1126](https://github.com/dlr-sc/tigl/issues/1126)).

- Fixes:
- In #1026, the tolerance for creating cells via contourCoordinate was reduced for cells in spanwise direction. That fixed a bug that caused cells to overlap potentially. The same reduction was missed in chordwise direction and is added now to avoid the cell overlap in chordwise direction, as well. (#1034)
- Fix inconsistent parametrization of super ellipse profiles. Before, the super ellipses ware parametrized by arc length, which resulted in
different parameters at the four distinct points of the super ellipse. Potential kinks in two super ellipses were not necessarily connected by a
v-isoline in the resulting loft. ([#1094](https://github.com/dlr-sc/tigl/issues/1094))
Expand Down
6 changes: 5 additions & 1 deletion bindings/python_internal/configuration.i
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
#include "CCPACSWingSparSegment.h"
#include "CCPACSWingRibsDefinition.h"
#include "CCPACSSkinSegment.h"
#include "CCPACSDuctStructure.h"
#include "CCPACSSkin.h"
#include "CTiglWingChordface.h"
#include "CCPACSCrossBeamAssemblyPosition.h"
#include "CCPACSCrossBeamStrutAssemblyPosition.h"
Expand Down Expand Up @@ -387,10 +389,12 @@ namespace tigl
// --------------- Fuselage structure -----------------//

%apply double *OUTPUT { double* eta, double* xsi };
%ignore tigl::CTiglStringerFrameBorderedObject;
%include "CTiglStringerFrameBorderedObject.h"
%include "generated/CPACSSkinSegment.h"
%include "CCPACSSkinSegment.h"
%boost_optional(tigl::CCPACSSkin)
%include "generated/CPACSSkin.h"
%include "CCPACSSkin.h"
%include "generated/CPACSCargoDoorsAssembly.h"
%include "generated/CPACSPressureBulkheadAssembly.h"
%include "CCPACSPressureBulkheadAssembly.h"
Expand Down
16 changes: 15 additions & 1 deletion src/fuselage/CTiglStringerFrameBorderedObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,21 @@ namespace tigl
{
CTiglStringerFrameBorderedObject::CTiglStringerFrameBorderedObject(
const CTiglUIDManager& uidMgr, const CTiglRelativelyPositionedComponent* parent, std::string& startFrameUID, std::string& endFrameUID,
std::string& startStringerUID, boost::variant<std::string&, boost::optional<std::string>&> endStringerUID)
std::string& startStringerUID, std::string& endStringerUID)
: m_uidMgr(uidMgr)
, m_parent(parent)
, m_startFrameUID(startFrameUID)
, m_endFrameUID(endFrameUID)
, m_startStringerUID(startStringerUID)
, m_endStringerUID(endStringerUID)
, m_borderCache(*this, &CTiglStringerFrameBorderedObject::UpdateBorders)
, m_geometry(*this, &CTiglStringerFrameBorderedObject::BuildGeometry)
{
}

CTiglStringerFrameBorderedObject::CTiglStringerFrameBorderedObject(
const CTiglUIDManager& uidMgr, const CTiglRelativelyPositionedComponent* parent, std::string& startFrameUID, std::string& endFrameUID,
std::string& startStringerUID, boost::optional<std::string>& endStringerUID)
: m_uidMgr(uidMgr)
, m_parent(parent)
, m_startFrameUID(startFrameUID)
Expand Down
7 changes: 6 additions & 1 deletion src/fuselage/CTiglStringerFrameBorderedObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ class CTiglStringerFrameBorderedObject
TIGL_EXPORT CTiglStringerFrameBorderedObject(const CTiglUIDManager& uidMgr, const CTiglRelativelyPositionedComponent* parent,
std::string& startFrameUID, std::string& endFrameUID,
std::string& startStringerUID,
boost::variant<std::string&, boost::optional<std::string>&> endStringerUID);
std::string& endStringerUID);

TIGL_EXPORT CTiglStringerFrameBorderedObject(const CTiglUIDManager& uidMgr, const CTiglRelativelyPositionedComponent* parent,
std::string& startFrameUID, std::string& endFrameUID,
std::string& startStringerUID,
boost::optional<std::string>& endStringerUID);

TIGL_EXPORT TopoDS_Shape GetGeometry(TiglCoordinateSystem referenceCS = GLOBAL_COORDINATE_SYSTEM) const;

Expand Down
20 changes: 11 additions & 9 deletions tests/TestData/fuselage_structure-v3.xml
Original file line number Diff line number Diff line change
Expand Up @@ -601,15 +601,17 @@
</stringerPosition>
</stringer>
</stringers>
<skinSegments>
<skinSegment uID="skinSegment1">
<sheetElementUID>TODO</sheetElementUID>
<startFrameUID>half_frame</startFrameUID>
<endFrameUID>frame1</endFrameUID>
<startStringerUID>stringer1</startStringerUID>
<endStringerUID>stringer7</endStringerUID>
</skinSegment>
</skinSegments>
<skin>
<skinSegments>
<skinSegment uID="skinSegment1">
<sheetElementUID>TODO</sheetElementUID>
<startFrameUID>half_frame</startFrameUID>
<endFrameUID>frame1</endFrameUID>
<startStringerUID>stringer1</startStringerUID>
<endStringerUID>stringer7</endStringerUID>
</skinSegment>
</skinSegments>
</skin>
<cargoDoors>
<cargoDoor uID="cargoDoor1">
<doorElementUID>TODO</doorElementUID>
Expand Down
143 changes: 143 additions & 0 deletions tests/python/test_fuselage_structure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#############################################################################
# Copyright (C) 2007-2026 German Aerospace Center (DLR/SC)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#############################################################################

from pathlib import Path
import unittest

from OCC.Core.TopoDS import TopoDS_Shape

from tigl3 import configuration, geometry
from tigl3.tigl3wrapper import Tigl3
from tixi3.tixi3wrapper import Tixi3


class TestFuselageStructureBindings(unittest.TestCase):
"""
Regression test for https://github.com/DLR-SC/tigl/issues/1037 :
CCPACSFuselageStructure.GetSkin() and the classes reachable from it
(CCPACSSkin, CPACSSkinSegments, CCPACSSkinSegment, ...) as well as the
other boost::optional-typed structure members were missing from the
Python bindings.
"""

CPACS_FILE = Path("TestData/fuselage_structure-v3.xml")
CONFIGURATION_UID = "FuselageStructureTest"

FUSELAGE_UID = "Fuselage"
SKIN_SEGMENT_UID = "skinSegment1"
CARGO_DOOR_UID = "cargoDoor1"

@classmethod
def setUpClass(cls) -> None:
super().setUpClass()

if not cls.CPACS_FILE.is_file():
raise FileNotFoundError(f"File not found: {cls.CPACS_FILE.resolve()}")

cls.tixi = Tixi3()
cls.tigl = Tigl3()
cls._tixi_is_open = False
cls._tigl_is_open = False

cls.addClassCleanup(cls._close_configuration)

tixi_result = cls.tixi.open(str(cls.CPACS_FILE))
if tixi_result is not None:
raise AssertionError(f"Tixi3.open() unexpectedly returned {tixi_result!r}")
cls._tixi_is_open = True

tigl_result = cls.tigl.open(cls.tixi, cls.CONFIGURATION_UID)
if tigl_result is not None:
raise AssertionError(f"Tigl3.open() unexpectedly returned {tigl_result!r}")
cls._tigl_is_open = True

manager = configuration.CCPACSConfigurationManager.get_instance()
cls.aircraft_config = manager.get_configuration(cls.tigl._handle.value)
cls.uid_manager = cls.aircraft_config.get_uidmanager()

cls.fuselage = cls.uid_manager.get_geometric_component(cls.FUSELAGE_UID)
cls.structure = cls.fuselage.get_structure()

@classmethod
def _close_configuration(cls) -> None:
try:
if cls._tigl_is_open:
cls.tigl.close()
finally:
if cls._tixi_is_open:
cls.tixi.close()

def test_skin_accessible(self) -> None:
# CCPACSFuselageStructure.get_skin() used to return an empty/unusable
# object because CCPACSSkin was not registered with SWIG at all.
skin = self.structure.get_skin()

self.assertIsInstance(skin, configuration.CCPACSSkin)

skin_segments = skin.get_skin_segments()
self.assertIsInstance(skin_segments, configuration.CPACSSkinSegments)

def test_skin_segment_geometry(self) -> None:
skin_segments = self.structure.get_skin().get_skin_segments()

self.assertEqual(skin_segments.get_skin_segment_count(), 1)

skin_segment = skin_segments.get_skin_segment(self.SKIN_SEGMENT_UID)
self.assertIsInstance(
skin_segment,
configuration.CCPACSSkinSegment,
)

# CCPACSSkinSegment.get_geometry() is inherited from
# CTiglStringerFrameBorderedObject, which used to be %ignore'd
# entirely in the SWIG config.
geom = skin_segment.get_geometry()
self.assertIsInstance(geom, TopoDS_Shape)
self.assertFalse(geom.IsNull())

def test_cargo_door_geometry(self) -> None:
# CCPACSDoorAssemblyPosition also derives from
# CTiglStringerFrameBorderedObject and was affected the same way.
cargo_doors = self.structure.get_cargo_doors()
self.assertIsInstance(cargo_doors, configuration.CPACSCargoDoorsAssembly)

door = cargo_doors.get_cargo_door(self.CARGO_DOOR_UID)
self.assertIsInstance(door, configuration.CCPACSDoorAssemblyPosition)

geom = door.get_geometry()
self.assertIsInstance(geom, TopoDS_Shape)
self.assertFalse(geom.IsNull())

# cargo doors also implement ITiglGeometricComponent, so they must be
# reachable (as their concrete type) via the UID manager factory too.
door_via_uidmanager = self.uid_manager.get_geometric_component(self.CARGO_DOOR_UID)
self.assertIsInstance(door_via_uidmanager, geometry.ITiglGeometricComponent)
self.assertIsInstance(door_via_uidmanager, configuration.CCPACSDoorAssemblyPosition)

def test_other_optional_structure_members(self) -> None:
# Other boost::optional<...> members of CCPACSFuselageStructure were
# reported as broken alongside the skin.
stringers = self.structure.get_stringers()
self.assertIsInstance(stringers, configuration.CCPACSStringersAssembly)
self.assertGreater(stringers.get_stringer_count(), 0)

frames = self.structure.get_frames()
self.assertIsInstance(frames, configuration.CCPACSFramesAssembly)
self.assertGreater(frames.get_frame_count(), 0)


if __name__ == "__main__":
unittest.main()
Loading