OpenRadioss 2025.1.11
OpenRadioss project
Loading...
Searching...
No Matches
cwipi_coupling_adapter.cpp
Go to the documentation of this file.
1//Copyright> OpenRadioss
2//Copyright> Copyright (C) 1986-2025 Altair Engineering Inc.
3//Copyright>
4//Copyright> This program is free software: you can redistribute it and/or modify
5//Copyright> it under the terms of the GNU Affero General Public License as published by
6//Copyright> the Free Software Foundation, either version 3 of the License, or
7//Copyright> (at your option) any later version.
8//Copyright>
9//Copyright> This program is distributed in the hope that it will be useful,
10//Copyright> but WITHOUT ANY WARRANTY; without even the implied warranty of
11//Copyright> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12//Copyright> GNU Affero General Public License for more details.
13//Copyright>
14//Copyright> You should have received a copy of the GNU Affero General Public License
15//Copyright> along with this program. If not, see <https://www.gnu.org/licenses/>.
16//Copyright>
17//Copyright>
18//Copyright> Commercial Alternative: Altair Radioss Software
19//Copyright>
20//Copyright> As an alternative to this open-source version, Altair also offers Altair Radioss
21//Copyright> software under a commercial license. Contact Altair to discuss further if the
22//Copyright> commercial version may interest you: https://www.altair.com/radioss/.
24#include <iostream>
25#include <fstream>
26#include <sstream>
27#include <algorithm>
28
29#ifdef WITH_CWIPI
30
31CwipiCouplingAdapter::CwipiCouplingAdapter()
32 : active_(false)
33 , maxTimeStepSize_(1e30)
34 , initialized_(false)
35 , dimension_(3)
36 , tolerance_(0.1)
37 , order_(1)
38 , numElements_(0)
39 , applicationName_("Radioss")
40 , coupledAppName_("Radioss")
41 , couplingName_("Coupling")
42 , exchangeName_("Exchange")
43{
44}
45
46CwipiCouplingAdapter::~CwipiCouplingAdapter() {
47 if (active_) {
48 finalize();
49 }
50}
51
52bool CwipiCouplingAdapter::configure(const std::string& configFile) {
53 // Expected format of the configuration file:
54 // /CWIPI/APPLICATION_NAME/SolverOne
55 // /CWIPI/COUPLED_APP_NAME/SolverTwo
56 // /CWIPI/READ/FORCES
57 // /CWIPI/WRITE/POSITIONS
58 // /CWIPI/SURF/8
59 std::cout << "Configuring CwipiCouplingAdapter with file: " << configFile << std::endl;
60 std::ifstream file(configFile);
61 if (!file.is_open()) {
62 std::cout << "No " << configFile << " file found in the current directory" << std::endl;
63 std::cout << "CWIPI will not be active" << std::endl;
64 active_ = false;
65 return false;
66 }
67
68 std::string line;
69 while (std::getline(file, line)) {
70 // Remove leading/trailing whitespace
71 line.erase(0, line.find_first_not_of(" \t"));
72 line.erase(line.find_last_not_of(" \t") + 1);
73
74 if (line.empty() || line[0] == '#') continue;
75
76 // Parse CWIPI configuration
77 // Expected format: /CWIPI/KEY/VALUE
78 std::vector<std::string> parts;
79 std::istringstream iss(line);
80 std::string part;
81
82 while (std::getline(iss, part, '/')) {
83 if (!part.empty()) {
84 parts.push_back(part);
85 }
86 }
87
88 if (parts.size() >= 3 && parts[0] == "CWIPI") {
89 std::string key = parts[1];
90 std::string value = parts[2];
91
92 // Handle cases where VALUE might contain additional path segments
93 if (parts.size() > 3) {
94 for (size_t i = 3; i < parts.size(); ++i) {
95 value += "/" + parts[i];
96 }
97 }
98 if (key == "APPLICATION_NAME") {
99 std::cout << "Setting applicationName_ to " << value << std::endl;
100 applicationName_ = value;
101 } else if (key == "COUPLED_APP_NAME") {
102 std::cout << "Setting coupledAppName_ to " << value << std::endl;
103 coupledAppName_ = value;
104 } else if (key == "COUPLING_NAME") {
105 couplingName_ = value;
106 } else if (key == "EXCHANGE_NAME") {
107 std::cout << "Setting exchangeName_ to " << value << std::endl;
108 exchangeName_ = value;
109 } else if (key == "DIMENSION") {
110 dimension_ = std::stoi(value);
111 } else if (key == "TOLERANCE") {
112 tolerance_ = std::stod(value);
113 } else if (key == "ORDER") {
114 order_ = std::stoi(value);
115 } else if (key == "READ") {
116 DataType dataType = stringToDataType(value);
117 if (dataType == DataType::NOTHING) {
118 std::cout << "Warning: Unknown data type '" << value << "' in read configuration." << std::endl;
119 continue;
120 }
121 readData_[static_cast<size_t>(dataType)].isActive = true;
122 if (DataType::POSITIONS == dataType) {
123 readData_[static_cast<size_t>(dataType)].mode = Mode::REPLACE;
124 } else if (DataType::FORCES == dataType) {
125 readData_[static_cast<size_t>(dataType)].mode = Mode::ADD;
126 }
127// std::cout << "Setting readData_[" << static_cast<size_t>(dataType)
128// << "].isActive to true for data type " << value << std::endl;
129 } else if (key == "WRITE") {
130 DataType dataType = stringToDataType(value);
131 if (dataType == DataType::NOTHING) {
132 std::cout << "Warning: Unknown data type '" << value << "' in write configuration." << std::endl;
133 continue;
134 }
135 writeData_[static_cast<size_t>(dataType)].isActive = true;
136 // std::cout << "Setting writeData_[" << static_cast<size_t>(dataType)
137 // << "].isActive to true for data type " << value << std::endl;
138 } else if (key == "GRNOD") {
139 // should not be used, interface should be defined by /CWIPI/SURF instead of a grnod with cwipi
140 setGroupNodeId(std::stoi(value));
141 std::cout << "Setting group_node_id_ to " << value << std::endl;
142 }
143 else if (key == "SURF") {
144 //surface_id_ = std::stoi(value);
145 setSurfaceId(std::stoi(value));
146 std::cout<<" Setting surface_id_ to " << value << std::endl;
147 } else {
148 std::cout << "Warning: Unknown CWIPI configuration key '" << key << "'." << std::endl;
149 }
150 }
151 }
152 // call initialize to set up the CWIPI environment
153 std::cout << "Initializing CWIPI with application name: " << applicationName_ << std::endl;
154 cwipi_init(MPI_COMM_WORLD, applicationName_.c_str(), &localComm_);
155
156 int currentRank, localCommSize;
157 MPI_Comm_rank(localComm_, &currentRank);
158 MPI_Comm_size(localComm_, &localCommSize);
159 std::cout << "CWIPI initialized on rank " << currentRank
160 << " with local communicator size: " << localCommSize << std::endl;
161
162
163 active_ = true;
164 return true;
165}
166
167
168void CwipiCouplingAdapter::setNodes(const std::vector<int>& nodeIds) {
169 couplingNodeIds_ = nodeIds;
170
171 // Allocate buffers for 3D data
172 int bufferSize = couplingNodeIds_.size() * 3;
173
174 for (size_t i = 0; i < readData_.size(); ++i) {
175 if (readData_[i].isActive) {
176 readData_[i].buffer.resize(bufferSize);
177 }
178 }
179
180 for (size_t i = 0; i < writeData_.size(); ++i) {
181 if (writeData_[i].isActive) {
182 writeData_[i].buffer.resize(bufferSize);
183 }
184 }
185}
186
187void CwipiCouplingAdapter::setMesh(const int* elem_node_offsets, const int* elem_node_indices, int num_elements) {
188 numElements_ = num_elements;
189 eltsConnecPointer_.assign(elem_node_offsets, elem_node_offsets + num_elements + 1);
190 eltsConnec_.assign(elem_node_indices, elem_node_indices + elem_node_offsets[num_elements]);
191}
192
193bool CwipiCouplingAdapter::initialize(const double* coordinates, int totalNodes, int mpiRank, int mpiSize) {
194 if (!active_) return false;
195
196 try {
197 // 1. Initialize CWIPI
198 std::cout << "Initializing CWIPI coupling adapter..." << std::endl;
199 // 2. Create coupling
200 cwipi_create_coupling(couplingName_.c_str(),
201 CWIPI_COUPLING_PARALLEL_WITH_PARTITIONING,
202 coupledAppName_.c_str(),
203 dimension_,
204 tolerance_,
205 CWIPI_STATIC_MESH,
206 CWIPI_SOLVER_CELL_VERTEX,
207 -1, // No postprocessing
208 "EnSight Gold",
209 "text");
210
211 // 3. Set up mesh vertices for coupling nodes
212 std::vector<double> meshVertices;
213 meshVertices.reserve(couplingNodeIds_.size() * 3);
214
215 for (int nodeId : couplingNodeIds_) {
216 // Convert from 1-based Fortran indexing to 0-based C++ indexing
217 int idx = nodeId - 1;
218 meshVertices.push_back(coordinates[idx * 3]);
219 meshVertices.push_back(coordinates[idx * 3 + 1]);
220 meshVertices.push_back(coordinates[idx * 3 + 2]);
221 }
222
223 // 4. Define mesh
224 if (order_ == 1) {
225 std::cout << "Defining CWIPI mesh with linear elements." << std::endl;
226 // couplingNodeIds_.size() should be equal to meshVertices.size() / 3
227 //std::cout << "Number of coupling nodes: " << couplingNodeIds_.size()
228 // << ", Number of mesh vertices: " << meshVertices.size() / 3 << std::endl;
229 cwipi_define_mesh(couplingName_.c_str(),
230 couplingNodeIds_.size(),
231 numElements_,
232 meshVertices.data(),
233 eltsConnecPointer_.data(),
234 eltsConnec_.data());
235 } else {
236 std::cout << "Defining CWIPI mesh with higher-order elements." << std::endl;
237 cwipi_ho_define_mesh(couplingName_.c_str(),
238 couplingNodeIds_.size(),
239 numElements_,
240 order_,
241 meshVertices.data(),
242 eltsConnecPointer_.data(),
243 eltsConnec_.data());
244 }
245
246 initialized_ = true;
247 cwipi_locate(couplingName_.c_str());
248 int nNotLocated = cwipi_get_n_not_located_points(couplingName_.c_str());
249 if (nNotLocated > 0) {
250 std::cout << "Warning: " << nNotLocated << " points not located in coupling "
251 << couplingName_ << std::endl;
252 }
253
254 } catch (const std::exception& e) {
255 std::cerr << "Error initializing CWIPI: " << e.what() << std::endl;
256 return false;
257 }
258
259 return true;
260}
261
262void CwipiCouplingAdapter::writeData(const double* values, int totalNodes, double dt, int dataType) {
263 if (!active_ || !initialized_) return;
264
265 if (!writeData_[static_cast<size_t>(dataType)].isActive) {
266 return;
267 }
268
269 // Extract data for coupling nodes
270 extractNodeData(values, totalNodes, dataType);
271
272 // Start async send
273 std::string fieldName = getFieldName(static_cast<DataType>(dataType));
274 int tag = getTag(static_cast<DataType>(dataType));
275 std::cout << "Sending data for field: " << fieldName
276 << " with tag: " << tag << std::endl;
277 cwipi_issend(couplingName_.c_str(),
278 exchangeName_.c_str(),
279 tag,
280 1, // time step (user controlled)
281 1, // n_step
282 0.0, // physical_time (user controlled)
283 fieldName.c_str(),
284 writeData_[dataType].buffer.data(),
285 &writeData_[dataType].sendRequest);
286
287 // Wait for send completion
288// std::cout << "Waiting for data send completion for field: " << fieldName << std::endl;
289 cwipi_wait_issend(couplingName_.c_str(), writeData_[dataType].sendRequest);
290
291
292// // Wait for any pending sends to complete
293// for (size_t i = 0; i < writeData_.size(); ++i) {
294// if (writeData_[i].isActive && writeData_[i].sendRequest != -1) {
295// std::cout << "Waiting for send completion for field: "
296// << getFieldName(static_cast<DataType>(i)) << std::endl;
297// cwipi_wait_issend(couplingName_.c_str(), writeData_[i].sendRequest);
298// writeData_[i].sendRequest = -1; // Reset request
299// }
300// }
301//
302}
303
304void CwipiCouplingAdapter::readData(double* values, int totalNodes, double dt, int dataType) {
305 if (!active_ || !initialized_) return;
306
307 if (!readData_[static_cast<size_t>(dataType)].isActive) {
308 return;
309 }
310
311 // Start async receive
312 std::string fieldName = getFieldName(static_cast<DataType>(dataType));
313 int tag = getTag(static_cast<DataType>(dataType));
314// std::cout << "Receiving data for field: " << fieldName
315// << " with tag: " << tag << std::endl;
316 cwipi_irecv(couplingName_.c_str(),
317 exchangeName_.c_str(),
318 tag,
319 1, // time step
320 1, // n_step
321 0.0, // physical_time
322 fieldName.c_str(),
323 readData_[dataType].buffer.data(),
324 &readData_[dataType].recvRequest);
325
326 // Wait for receive completion
327// std::cout << "Waiting for data reception for field: " << fieldName << std::endl;
328 cwipi_wait_irecv(couplingName_.c_str(), readData_[dataType].recvRequest);
329// std::cout<<"COMPLETED irecv for " << fieldName << std::endl;
330 //std::cout << "Checking for not located points in coupling: " << couplingName_ << std::endl;
331
332 // Inject received data into global arrays
333 injectNodeData(values, totalNodes, dataType);
334}
335
336void CwipiCouplingAdapter::advance(double& dt) {
337 if (!active_ || !initialized_) return;
338
339 // Trigger location/interpolation update
340// std::cout << "Advancing CWIPI coupling adapter with dt: " << dt << std::endl;
341 cwipi_locate(couplingName_.c_str());
342 int nNotLocated = cwipi_get_n_not_located_points(couplingName_.c_str());
343 if (nNotLocated > 0) {
344 std::cout << "Warning: " << nNotLocated << " points not located in coupling "
345 << couplingName_ << std::endl;
346 }
347
348}
349
350bool CwipiCouplingAdapter::isCouplingOngoing() const {
351 if (!active_ || !initialized_)
352 {
353 std::cout << "Warning: Coupling is not active or not initialized." << std::endl;
354 std::cout<<" active_: " << active_ << ", initialized_: " << initialized_ << std::endl;
355 return false; // Not active or not initialized
356 }
357
358 // CWIPI doesn't have an inherent "ongoing" state like preCICE
359 // Return true if coupling is active and initialized
360 return true;
361}
362
363bool CwipiCouplingAdapter::requiresWritingCheckpoint() const {
364 return false; // CWIPI typically doesn't require checkpoints
365}
366
367bool CwipiCouplingAdapter::requiresReadingCheckpoint() const {
368 return false; // CWIPI typically doesn't require checkpoints
369}
370
371void CwipiCouplingAdapter::finalize() {
372 if (!active_) return;
373
374 if (initialized_) {
375 // Delete coupling
376 std::cout << "Finalizing CWIPI coupling adapter..." << std::endl;
377 cwipi_delete_coupling(couplingName_.c_str());
378
379 // Finalize CWIPI
380 cwipi_finalize();
381 initialized_ = false;
382 }
383
384 // Clear all data structures
385 couplingNodeIds_.clear();
386 eltsConnecPointer_.clear();
387 eltsConnec_.clear();
388
389 for (auto& data : readData_) {
390 data.buffer.clear();
391 data.isActive = false;
392 data.sendRequest = -1;
393 data.recvRequest = -1;
394 }
395 for (auto& data : writeData_) {
396 data.buffer.clear();
397 data.isActive = false;
398 data.sendRequest = -1;
399 data.recvRequest = -1;
400 }
401
402 active_ = false;
403 return;
404}
405
406bool CwipiCouplingAdapter::isActive() const {
407 return active_;
408}
409
410double CwipiCouplingAdapter::getMaxTimeStepSize() const {
411 return maxTimeStepSize_;
412}
413
414int CwipiCouplingAdapter::getNumberOfCouplingNodes() const {
415 return couplingNodeIds_.size();
416}
417
418void CwipiCouplingAdapter::extractNodeData(const double* globalValues, int totalNodes, int dataType) {
419 if (!writeData_[dataType].isActive) {
420 return;
421 }
422
423 for (size_t i = 0; i < couplingNodeIds_.size(); ++i) {
424 int nodeId = couplingNodeIds_[i] - 1; // Convert to 0-based indexing
425 writeData_[dataType].buffer[i * 3] = globalValues[nodeId * 3];
426 writeData_[dataType].buffer[i * 3 + 1] = globalValues[nodeId * 3 + 1];
427 writeData_[dataType].buffer[i * 3 + 2] = globalValues[nodeId * 3 + 2];
428 }
429}
430
431void CwipiCouplingAdapter::injectNodeData(double* globalValues, int totalNodes, int dataType) {
432 if (!readData_[dataType].isActive) {
433 std::cout << "Warning: Attempted to read data for inactive data type "
434 << getFieldName(static_cast<DataType>(dataType)) << std::endl;
435 return;
436 }
437
438 if (readData_[dataType].mode == Mode::ADD) {
439 for (size_t i = 0; i < couplingNodeIds_.size(); ++i) {
440 int nodeId = couplingNodeIds_[i] - 1; // Convert to 0-based indexing
441 globalValues[nodeId * 3] += readData_[dataType].buffer[i * 3];
442 globalValues[nodeId * 3 + 1] += readData_[dataType].buffer[i * 3 + 1];
443 globalValues[nodeId * 3 + 2] += readData_[dataType].buffer[i * 3 + 2];
444 }
445 } else if (readData_[dataType].mode == Mode::REPLACE) {
446 for (size_t i = 0; i < couplingNodeIds_.size(); ++i) {
447 int nodeId = couplingNodeIds_[i] - 1; // Convert to 0-based indexing
448 globalValues[nodeId * 3] = readData_[dataType].buffer[i * 3];
449 globalValues[nodeId * 3 + 1] = readData_[dataType].buffer[i * 3 + 1];
450 globalValues[nodeId * 3 + 2] = readData_[dataType].buffer[i * 3 + 2];
451 }
452 }
453
454}
455
456std::string CwipiCouplingAdapter::getFieldName(DataType type) {
457 switch(type) {
458 case DataType::POSITIONS: return "positions";
459 case DataType::FORCES: return "forces";
460 case DataType::DISPLACEMENTS: return "displacements";
461 default: return "unknown";
462 }
463}
464
465int CwipiCouplingAdapter::getTag(DataType type) {
466 return static_cast<int>(type);
467}
468
469#endif
LIBSEQ_INT LIBSEQ_CALL MPI_Comm_rank(LIBSEQ_INT comm, LIBSEQ_INT *rank)
Definition mpic.c:23
static MPI_Comm MPI_COMM_WORLD
Definition mpi.h:51