Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding ability to tag node ids in MPhys TacsBuilder #287

Merged
merged 7 commits into from
Feb 9, 2024
Merged
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
25 changes: 21 additions & 4 deletions tacs/mphys/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,8 @@ def get_tagged_indices(self, tags):

Parameters
----------
tags : list[str]
tags : list[str, int]
list of component names or node IDs to include in body

Returns
-------
Expand All @@ -436,13 +437,29 @@ def get_tagged_indices(self, tags):
# Select all node IDs
masked_local_nodes = np.arange(nnodes)

# Get the compIDs associated with tags
# Get the node IDs associated with tags
else:
tagged_comps = self.fea_assembler.selectCompIDs(include=tags)
# Select local node IDs for tags
# Pick out any component names in supplied tags
comp_names = [comp_name for comp_name in tags if isinstance(comp_name, str)]
tagged_comps = self.fea_assembler.selectCompIDs(include=comp_names)
# Select local node IDs for components
masked_local_nodes = self.fea_assembler.getLocalNodeIDsForComps(
tagged_comps
)
# Pick out any node IDs in supplied tags
global_node_ids = [
comp_name
for comp_name in tags
if isinstance(comp_name, (int, np.integer))
]
# Select local node IDs from global node IDs
local_node_ids = self.fea_assembler.getLocalNodeIDsFromGlobal(
global_node_ids, nastranOrdering=True
)
# getLocalNodeIDsFromGlobal returns -1 for nodes not on this processor, so remove those
local_node_ids[:] = [id for id in local_node_ids if id >= 0]
masked_local_nodes += local_node_ids
masked_local_nodes = np.unique(masked_local_nodes)

# Select local node IDs and multiplier node IDs
local_mnodes = self.fea_assembler.getLocalMultiplierNodeIDs()
Expand Down
35 changes: 31 additions & 4 deletions tacs/pytacs.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ def getCompNames(self, compIDs=None):
if compIDs is None:
return copy.deepcopy(self.compDescripts)
# Convert to list
elif isinstance(compIDs, int):
elif isinstance(compIDs, (int, np.integer)):
compIDs = [compIDs]
# Make sure list is flat
else:
Expand Down Expand Up @@ -744,6 +744,31 @@ def getLocalNodeIDsForComps(self, compIDs):

return self.meshLoader.getLocalNodeIDsForComps(compIDs)

def getLocalNodeIDsFromGlobal(self, globalIDs, nastranOrdering=False):
"""
Given a list of node IDs in global (non-partitioned) ordering
returns the local (partitioned) node IDs on each processor.
If a requested node is not included on this processor,
an entry of -1 will be returned.

Parameters
----------
globalIDs : int or list[int]
List of global node IDs.

nastranOrdering : bool
Flag signaling whether globalIDs is in TACS (default) or NASTRAN (grid IDs in bdf file) ordering
Defaults to False.

Returns
-------
localIDs : list[int]
List of local node IDs for each entry in globalIDs.
If the node is not owned by this processor, its index is filled with a value of -1.
"""

return self.meshLoader.getLocalNodeIDsFromGlobal(globalIDs, nastranOrdering)

def initialize(self, elemCallBack=None):
"""
This is the 'last' method to be called during the setup. The
Expand Down Expand Up @@ -857,10 +882,12 @@ def _checkNonlinearity(self) -> bool:
# Check if (res2-res0) - 2 * (res1 - res0) is zero (or very close to it)
resNorm = np.real(res1.norm())
res2.axpy(-2.0, res1)
if resNorm == 0.0 or (np.real(res2.norm()) / resNorm) <= self.getOption("linearityTol"):
return False # not nonlinear case
if resNorm == 0.0 or (np.real(res2.norm()) / resNorm) <= self.getOption(
"linearityTol"
):
return False # not nonlinear case
else:
return True # nonlinear case
return True # nonlinear case

def _elemCallBackFromBDF(self):
"""
Expand Down
56 changes: 47 additions & 9 deletions tests/integration_tests/test_mphys_struct_plate.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,19 @@ def constraint_setup(scenario_name, fea_assembler, constraints):
constr_list = [constr]
return constr_list

tacs_builder = tacs.mphys.TacsBuilder(
mesh_file=bdf_file,
element_callback=element_callback,
problem_setup=problem_setup,
constraint_setup=constraint_setup,
check_partials=True,
coupled=True,
write_solution=False,
)
self.tacs_builder = tacs_builder

class Top(Multipoint):
def setup(self):
tacs_builder = tacs.mphys.TacsBuilder(
mesh_file=bdf_file,
element_callback=element_callback,
problem_setup=problem_setup,
constraint_setup=constraint_setup,
check_partials=True,
coupled=True,
write_solution=False,
)
tacs_builder.initialize(self.comm)

dvs = self.add_subsystem("dvs", om.IndepVarComp(), promotes=["*"])
Expand Down Expand Up @@ -154,3 +156,39 @@ def setup_funcs(self):
to test their sensitivities with respect to.
"""
return FUNC_REFS, wrt

def test_get_tagged_indices(self):
"""
Test the get_tagged_indices method
"""
prob = self.setup_problem(dtype=float)
prob.setup()

# We want to test that:
# - For each comp_id, get_tagged_indices returns the same indices as `getLocalNodeIDsForComps`
# - For a random set of the NASTRAN node IDs, get_tagged_indices returns the corresponding local indices
# - For a mix of comp_ids and NASTRAN node IDs, get_tagged_indices returns the correct local indices
FEAAssembler = self.tacs_builder.get_fea_assembler()
compIDs = FEAAssembler.selectCompIDs()[0]
meshloader = FEAAssembler.meshLoader

for compID in compIDs:
trueNodeIDs = FEAAssembler.getLocalNodeIDsForComps([compID])
compName = FEAAssembler.getCompNames(compID)
taggedIndIDs = self.tacs_builder.get_tagged_indices(compName)
self.assertEqual(sorted(trueNodeIDs), sorted(taggedIndIDs))

nastranIDs = meshloader.getGlobalNodeIDsForComps(
[compID], nastranOrdering=True
)
taggedIndIDs = self.tacs_builder.get_tagged_indices(nastranIDs)
self.assertEqual(sorted(trueNodeIDs), sorted(taggedIndIDs))

# now test a mix of comp_ids and NASTRAN node IDs, we'll use the name of the first compID and the NASTRAN node
# IDs of the last compID
tags = FEAAssembler.getCompNames(
compIDs[0]
) + meshloader.getGlobalNodeIDsForComps([compIDs[-1]], nastranOrdering=True)
trueNodeIDs = FEAAssembler.getLocalNodeIDsForComps([compIDs[0], compIDs[-1]])
taggedIndIDs = self.tacs_builder.get_tagged_indices(tags)
self.assertEqual(sorted(trueNodeIDs), sorted(taggedIndIDs))
Loading