Skip to content

Commit

Permalink
fix aero loads (were almost zero before) (#250)
Browse files Browse the repository at this point in the history
  • Loading branch information
sean-engelstad authored Nov 6, 2023
1 parent 37d7269 commit 6df852e
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 172 deletions.
8 changes: 7 additions & 1 deletion funtofem/driver/oneway_struct_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ def prime_loads_from_file(
nprocs,
transfer_settings,
external_shape=False,
init_transfer=False,
):
"""
Used to prime aero loads for optimization over tacs analysis with shape change and tacs aim
Expand All @@ -242,6 +243,8 @@ def prime_loads_from_file(
Interface object from TACS to ESP/CAPS, wraps the tacsAIM object.
external_shape: bool
whether the tacs aim shape analysis is performed outside this class
timing_file: str or path
path to funtofem timing file statistics
"""
comm = solvers.comm
world_rank = comm.Get_rank()
Expand Down Expand Up @@ -271,12 +274,15 @@ def prime_loads_from_file(
body.initialize_variables(scenario)
body._distribute_aero_loads(loads_data)

return cls(
tacs_driver = cls(
solvers,
model,
nprocs=nprocs,
external_shape=external_shape,
)
if init_transfer:
tacs_driver._transfer_fixed_aero_loads()
return tacs_driver

@property
def manager(self, hot_start: bool = False):
Expand Down
31 changes: 22 additions & 9 deletions funtofem/model/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -1598,18 +1598,31 @@ def _distribute_aero_loads(self, data):
"""
distribute the aero loads and heat flux from a loads file
"""
print(f"F2F - starting to distribute loads")

for scenario_id in data:
scenario_data = data[scenario_id]

# create a dict for this entry
scenario_entry_dict = {}
for entry in scenario_data:
for ind, aero_id in enumerate(self.aero_id):
if entry["aeroID"] == aero_id and entry["bodyName"] == self.name:
if self.transfer is not None:
self.aero_loads[scenario_id][3 * ind : 3 * ind + 3] = entry[
"load"
]
if self.thermal_transfer is not None:
self.aero_heat_flux[scenario_id][ind] = entry["hflux"]
break
if entry["bodyName"] == self.name:
scenario_entry_dict[entry["aeroID"]] = {
"load": entry["load"],
"hflux": entry["hflux"],
}

for ind, aero_id in enumerate(self.aero_id):
if self.transfer is not None:
self.aero_loads[scenario_id][
3 * ind : 3 * ind + 3
] = scenario_entry_dict[aero_id]["load"]
if self.thermal_transfer is not None:
self.aero_heat_flux[scenario_id][ind] = scenario_entry_dict[
aero_id
]["hflux"]

print(f"F2F - done distribute loads")

def _collect_aero_mesh(self, comm, root=0):
"""
Expand Down
206 changes: 103 additions & 103 deletions funtofem/model/funtofem_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,9 +400,9 @@ def evaluate_composite_functions(self, compute_grad=True):
composite_func.evaluate_gradient()
return

def write_aero_loads(self, comm, filename, root=0):
def read_aero_loads(self, comm, filename, root=0):
"""
Write the aerodynamic loads file for the OnewayStructDriver.
Read the aerodynamic loads file for the OnewayStructDriver.
This file contains the following information:
Expand All @@ -429,50 +429,81 @@ def write_aero_loads(self, comm, filename, root=0):
root: int
The rank of the processor that will write the file
"""
loads_data = None
mesh_data = None

if comm.rank == root:
data = ""
# Specify the number of scenarios in file
data += f"{len(self.bodies)} {len(self.scenarios)} \n"
data += "aeromesh" + "\n"
scenario_data = None
loads_data = {}
mesh_data = {}

with open(filename, "r") as fp:
for line in fp.readlines():
entries = line.strip().split(" ")
# print("==> entries: ", entries)
if len(entries) == 2:
assert int(entries[1]) == len(self.scenarios)
assert int(entries[0]) == len(self.bodies)

elif len(entries) == 3 and entries[0] == "scenario":
matching_scenario = False
for scenario in self.scenarios:
if str(scenario.name).strip() == str(entries[2]).strip():
matching_scenario = True
break
assert matching_scenario
if scenario_data is not None:
loads_data[scenario.id] = scenario_data
scenario_data = []
elif len(entries) == 4 and entries[0] == "body_mesh":
body_name = entries[2]
mesh_data[body_name] = {"aeroID": [], "aeroX": []}
elif len(entries) == 4 and entries[0] != "body":
mesh_data[body_name]["aeroID"] += [entries[0]]
mesh_data[body_name]["aeroX"] += entries[1:4]

elif len(entries) == 5:
entry = {
"bodyName": body_name,
"aeroID": entries[0],
"load": entries[1:4],
"hflux": entries[4],
}
scenario_data.append(entry)

loads_data[scenario.id] = scenario_data

loads_data = comm.bcast(loads_data, root=root)
mesh_data = comm.bcast(mesh_data, root=root)

# initialize the mesh data
for body in self.bodies:
global_aero_x = np.array(mesh_data[body.name]["aeroX"])
global_aero_ids = np.array(mesh_data[body.name]["aeroID"])

body_ind = np.array([_ for _ in range(len(global_aero_ids))])
if comm.rank == root:
data += f"body_mesh {body.id} {body.name} {body.aero_nnodes} \n"
split_body_ind = np.array_split(body_ind, comm.Get_size())
else:
split_body_ind = None

id, aeroX = body._collect_aero_mesh(comm, root=root)
local_body_ind = comm.scatter(split_body_ind, root=root)

if comm.rank == root:
for i in range(len(id)):
data += "{} {} {} {} \n".format(
int(id[i]),
aeroX[3 * i + 0].real,
aeroX[3 * i + 1].real,
aeroX[3 * i + 2].real,
)
if comm.rank == root:
data += f"aeroloads \n"
local_aero_ids = global_aero_ids[local_body_ind]

for scenario in self.scenarios:
if comm.rank == root:
data += f"scenario {scenario.id} {scenario.name} \n"
aero_x_ind = (
[3 * i for i in local_body_ind]
+ [3 * i + 1 for i in local_body_ind]
+ [3 * i + 2 for i in local_body_ind]
)
aero_x_ind = sorted(aero_x_ind)

for body in self.bodies:
id, hflux, load = body._collect_aero_loads(comm, scenario, root=root)
local_aero_x = list(global_aero_x[aero_x_ind])

if comm.rank == root:
data += f"body {body.id} {body.name} {body.aero_nnodes} \n"
for i in range(len(id)):
data += "{} {} {} {} {} \n".format(
int(id[i]),
load[3 * i + 0].real,
load[3 * i + 1].real,
load[3 * i + 2].real,
float(hflux[i].real),
)
body.initialize_aero_nodes(local_aero_x, local_aero_ids)

with open(filename, "w") as fp:
fp.write(data)
return
# return the loads data
return loads_data

def write_struct_loads(self, comm, filename, root=0):
"""
Expand Down Expand Up @@ -528,9 +559,9 @@ def write_struct_loads(self, comm, filename, root=0):
fp.write(data)
return

def read_aero_loads(self, comm, filename, root=0):
def write_aero_loads(self, comm, filename, root=0):
"""
Read the aerodynamic loads file for the OnewayStructDriver.
Write the aerodynamic loads file for the OnewayStructDriver.
This file contains the following information:
Expand All @@ -557,81 +588,50 @@ def read_aero_loads(self, comm, filename, root=0):
root: int
The rank of the processor that will write the file
"""
loads_data = None
mesh_data = None

if comm.rank == root:
scenario_data = None
loads_data = {}
mesh_data = {}

with open(filename, "r") as fp:
for line in fp.readlines():
entries = line.strip().split(" ")
# print("==> entries: ", entries)
if len(entries) == 2:
assert int(entries[1]) == len(self.scenarios)
assert int(entries[0]) == len(self.bodies)

elif len(entries) == 3 and entries[0] == "scenario":
matching_scenario = False
for scenario in self.scenarios:
if str(scenario.name).strip() == str(entries[2]).strip():
matching_scenario = True
break
assert matching_scenario
if scenario_data is not None:
loads_data[scenario.id] = scenario_data
scenario_data = []
elif len(entries) == 4 and entries[0] == "body_mesh":
body_name = entries[2]
mesh_data[body_name] = {"aeroID": [], "aeroX": []}
elif len(entries) == 4 and entries[0] != "body":
mesh_data[body_name]["aeroID"] += [entries[0]]
mesh_data[body_name]["aeroX"] += entries[1:4]

elif len(entries) == 5:
entry = {
"bodyName": body_name,
"aeroID": entries[0],
"load": entries[1:4],
"hflux": entries[4],
}
scenario_data.append(entry)

loads_data[scenario.id] = scenario_data

loads_data = comm.bcast(loads_data, root=root)
mesh_data = comm.bcast(mesh_data, root=root)
data = ""
# Specify the number of scenarios in file
data += f"{len(self.bodies)} {len(self.scenarios)} \n"
data += "aeromesh" + "\n"

# initialize the mesh data
for body in self.bodies:
global_aero_x = np.array(mesh_data[body.name]["aeroX"])
global_aero_ids = np.array(mesh_data[body.name]["aeroID"])

body_ind = np.array([_ for _ in range(len(global_aero_ids))])
if comm.rank == root:
split_body_ind = np.array_split(body_ind, comm.Get_size())
else:
split_body_ind = None
data += f"body_mesh {body.id} {body.name} {body.aero_nnodes} \n"

local_body_ind = comm.scatter(split_body_ind, root=root)
id, aeroX = body._collect_aero_mesh(comm, root=root)

local_aero_ids = global_aero_ids[local_body_ind]
if comm.rank == root:
for i in range(len(id)):
data += "{} {} {} {} \n".format(
int(id[i]),
aeroX[3 * i + 0].real,
aeroX[3 * i + 1].real,
aeroX[3 * i + 2].real,
)
if comm.rank == root:
data += f"aeroloads \n"

aero_x_ind = (
[3 * i for i in local_body_ind]
+ [3 * i + 1 for i in local_body_ind]
+ [3 * i + 2 for i in local_body_ind]
)
aero_x_ind = sorted(aero_x_ind)
for scenario in self.scenarios:
if comm.rank == root:
data += f"scenario {scenario.id} {scenario.name} \n"

local_aero_x = list(global_aero_x[aero_x_ind])
for body in self.bodies:
id, hflux, load = body._collect_aero_loads(comm, scenario, root=root)

body.initialize_aero_nodes(local_aero_x, local_aero_ids)
if comm.rank == root:
data += f"body {body.id} {body.name} {body.aero_nnodes} \n"
for i in range(len(id)):
data += "{} {} {} {} {} \n".format(
int(id[i]),
load[3 * i + 0].real,
load[3 * i + 1].real,
load[3 * i + 2].real,
float(hflux[i].real),
)

# return the loads data
return loads_data
with open(filename, "w") as fp:
fp.write(data)
return

def write_sensitivity_file(
self, comm, filename, discipline="aerodynamic", root=0, write_dvs: bool = True
Expand Down
Loading

0 comments on commit 6df852e

Please sign in to comment.