diff --git a/CMakeLists.txt b/CMakeLists.txt
index ea3c8ceb..eacdeb4b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -121,7 +121,7 @@ add_test(NAME read_write_test COMMAND test-precice-aste WORKING_DIRECTORY "${CM
# Detect and register examples as tests
-set(_examples lci_2d lci_3d nn nng_scalar nng_vector mapping_tester mapping_tester_serial mapping_tester_runtime mapping_tester_conv replay_mode)
+set(_examples lci_2d lci_3d nn nn_just_in_time nng_scalar nng_vector mapping_tester mapping_tester_serial mapping_tester_runtime mapping_tester_conv replay_mode)
foreach(example IN LISTS _examples)
add_test(NAME aste.example.${example}.setup
diff --git a/examples/mapping_tester/setup-test.json b/examples/mapping_tester/setup-test.json
index b61943d1..af49d539 100644
--- a/examples/mapping_tester/setup-test.json
+++ b/examples/mapping_tester/setup-test.json
@@ -33,8 +33,12 @@
"executor": "cpu",
"executor-options": ""
},
+ "np": {
+ "kind": "nearest-projection"
+ },
"nn": {
- "kind": "nearest-neighbor"
+ "kind": "nearest-neighbor",
+ "batch-size": "5"
}
}
},
diff --git a/examples/nn/precice-config.xml b/examples/nn/precice-config.xml
index 58a057c3..926adaf5 100644
--- a/examples/nn/precice-config.xml
+++ b/examples/nn/precice-config.xml
@@ -26,6 +26,9 @@
+
diff --git a/examples/nn_just_in_time/clean.sh b/examples/nn_just_in_time/clean.sh
new file mode 100755
index 00000000..e2feb93d
--- /dev/null
+++ b/examples/nn_just_in_time/clean.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+set -e -x
+
+rm -f -r precice-run
+rm -f -r precice-profiling
+rm -f profiling.json
+rm -f trace.json
+rm -f *.log
+rm -f map*.vtk
+rm -f fine_mesh_*.vtk
diff --git a/examples/nn_just_in_time/precice-config.xml b/examples/nn_just_in_time/precice-config.xml
new file mode 100644
index 00000000..e5d7f855
--- /dev/null
+++ b/examples/nn_just_in_time/precice-config.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/nn_just_in_time/run.sh b/examples/nn_just_in_time/run.sh
new file mode 100755
index 00000000..2044f154
--- /dev/null
+++ b/examples/nn_just_in_time/run.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+set -e -x
+
+# Calculate franke function on fine mesh
+precice-aste-evaluate -m ../fine_mesh.vtk -f "franke3d" -d "Franke Function" -o "fine_mesh_nn.vtk"
+
+# Map from the finer mesh to coarser mesh
+precice-aste-run -v -p A --mesh fine_mesh_nn --data "Franke Function" &
+precice-aste-run -v -p B --mesh ../coarse_mesh --output map_nn --data "InterpolatedData" --read-just-in-time 0
+
+# Calculate statistics
+precice-aste-evaluate -m map_nn.vtk -f "franke3d" -d difference --diffdata "InterpolatedData" --diff
diff --git a/src/common.cpp b/src/common.cpp
index 2645df2d..77afd78c 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -22,7 +22,7 @@ OptionMap getOptions(int argc, char *argv[])
"precice-aste-run will look for timeseries as well as distributed meshes (e.g. from preCICE exports) "
"automatically and load them if required.")(
"output", po::value(),
- "Output file name.")("vector", po::bool_switch(), "Distinguish between vector valued data and scalar data")("verbose,v", po::bool_switch(), "Enable verbose output")("all,a", po::bool_switch(), "Enable output from secondary ranks");
+ "Output file name.")("vector", po::bool_switch(), "Distinguish between vector valued data and scalar data")("verbose,v", po::bool_switch(), "Enable verbose output")("all,a", po::bool_switch(), "Enable output from secondary ranks")("read-just-in-time,jit", po::value(), "Instead of the conventional mapping in preCICE, make use of a just-in-time mapping, where values are read in batches of values.");
po::variables_map vm;
diff --git a/src/modes.cpp b/src/modes.cpp
index f07994c7..03feba6a 100644
--- a/src/modes.cpp
+++ b/src/modes.cpp
@@ -146,6 +146,15 @@ void aste::runMapperMode(const aste::ExecutionContext &context, const OptionMap
const bool isVector = options["vector"].as();
const std::string preciceConfig = options["precice-config"].as();
+ constexpr int conventionalReadData = -1;
+ // potentially make use of a just-in-time mapping using a prescribed batch size
+ const int batchSize = options.count("read-just-in-time")
+ ? options["read-just-in-time"].as()
+ : conventionalReadData;
+ if (batchSize < -1) {
+ ASTE_ERROR << "Batch size needs to be greater or equal to zero.";
+ }
+
addLogIdentity(participantName, context.rank);
ASTE_INFO << "ASTE Running in mapping test mode";
@@ -186,7 +195,11 @@ void aste::runMapperMode(const aste::ExecutionContext &context, const OptionMap
asteConfiguration.participantName = "B";
asteConfiguration.preciceConfigFilename = preciceConfig;
aste::asteInterface asteInterface;
- asteInterface.meshName = "B-Mesh";
+ if (batchSize == conventionalReadData) {
+ asteInterface.meshName = "B-Mesh";
+ } else {
+ asteInterface.meshName = "A-Mesh";
+ }
asteInterface.meshFilePrefix = meshname;
asteInterface.meshes = aste::BaseName(meshname).findAll(context);
@@ -212,7 +225,14 @@ void aste::runMapperMode(const aste::ExecutionContext &context, const OptionMap
asteInterface.meshes.front().loadMesh(asteInterface.mesh, dim, requireConnectivity);
asteInterface.meshes.front().loadData(asteInterface.mesh);
ASTE_INFO << "The loaded mesh " << asteInterface.meshes.front().filename() << " contains: " << asteInterface.mesh.summary();
- auto vertexIDs = aste::setupMesh(preciceInterface, asteInterface.mesh, asteInterface.meshName);
+
+ std::vector vertexIDs;
+ std::vector coordinates;
+ if (batchSize == conventionalReadData) {
+ vertexIDs = aste::setupMesh(preciceInterface, asteInterface.mesh, asteInterface.meshName);
+ } else {
+ coordinates = aste::setupDirectMeshAccess(preciceInterface, asteInterface.mesh, asteInterface.meshName, context);
+ }
ASTE_DEBUG << "Mesh setup completed on Rank " << context.rank;
if (preciceInterface.requiresInitialData()) {
@@ -263,8 +283,33 @@ void aste::runMapperMode(const aste::ExecutionContext &context, const OptionMap
for (auto &asteInterface : asteConfiguration.asteInterfaces) {
for (auto &meshdata : asteInterface.mesh.meshdata) {
if (meshdata.type == aste::datatype::READ) {
- meshdata.dataVector.resize(vertexIDs.size() * meshdata.numcomp);
- preciceInterface.readData(asteInterface.meshName, "Data", vertexIDs, dt, meshdata.dataVector);
+ if (batchSize == conventionalReadData) {
+ meshdata.dataVector.resize(vertexIDs.size() * meshdata.numcomp);
+ preciceInterface.readData(asteInterface.meshName, "Data", vertexIDs, dt, meshdata.dataVector);
+ } else {
+ int dim = preciceInterface.getMeshDimensions(asteInterface.meshName);
+ int nVertices = coordinates.size() / dim;
+ meshdata.dataVector.resize(nVertices * meshdata.numcomp);
+
+ // pass everything in one go
+ if (batchSize == 0) {
+ preciceInterface.mapAndReadData(asteInterface.meshName, "Data", coordinates, dt, meshdata.dataVector);
+ } else { // pass in batches
+ for (int i = 0; i < nVertices; i += batchSize) {
+ int availableVertices = std::min(i + batchSize, nVertices);
+ int coordStart = i * dim;
+ int coordEnd = availableVertices * dim;
+ int dataStart = i * meshdata.numcomp;
+ int dataEnd = availableVertices * meshdata.numcomp;
+
+ ::precice::span coordinateBatch(coordinates.data() + coordStart, coordEnd - coordStart);
+ ::precice::span dataBatch(meshdata.dataVector.data() + dataStart, dataEnd - dataStart);
+
+ // Call the API function with the current batch
+ preciceInterface.mapAndReadData(asteInterface.meshName, "Data", coordinateBatch, dt, dataBatch);
+ }
+ }
+ }
ASTE_DEBUG << "Data read: " << asteInterface.mesh.previewData(meshdata);
}
}
diff --git a/src/utilities.cpp b/src/utilities.cpp
index 9e1d65cc..a10306d9 100644
--- a/src/utilities.cpp
+++ b/src/utilities.cpp
@@ -72,6 +72,51 @@ void aste::setupEdgeIDs(precice::Participant &interface, const aste::Mesh &mesh,
}
}
+std::vector aste::setupDirectMeshAccess(precice::Participant &interface, const aste::Mesh &mesh, const std::string &meshName, const aste::ExecutionContext &exec)
+{
+ ASTE_DEBUG << "Setting up direct access for: " << meshName;
+ ASTE_DEBUG << "Setting up coordinates vector for mesh...";
+
+ const auto dim = interface.getMeshDimensions(meshName);
+ const auto nvertices = mesh.positions.size();
+ std::vector coordinates(dim * nvertices);
+ for (unsigned long i = 0; i < nvertices; ++i) {
+ const auto &pos = mesh.positions[i];
+ assert(pos.size() == static_cast(dim));
+ std::copy(pos.begin(), pos.end(), &coordinates[i * dim]);
+ }
+
+ ASTE_DEBUG << "Computing the (rank-local) bounding box:";
+
+ std::vector bb(2 * dim);
+
+ // for parallel runs, we define a proper bounding box
+ if (exec.isParallel()) {
+ // Initialize min and max values
+ for (int i = 0; i < dim; ++i) {
+ bb[2 * i] = std::numeric_limits::max(); // min values
+ bb[2 * i + 1] = std::numeric_limits::lowest(); // max values
+ }
+
+ // Iterate through the coordinates
+ for (size_t i = 0; i < coordinates.size(); i += dim) {
+ for (int j = 0; j < dim; ++j) {
+ double coord = coordinates[i + j];
+ bb[2 * j] = std::min(coord - 1e-12, bb[2 * j]); // Update min
+ bb[2 * j + 1] = std::max(coord + 1e-12, bb[2 * j + 1]); // Update max
+ }
+ }
+ } else {
+ // for serial runs, we just take everything (to account for potential discretization imbalances)
+ for (int i = 0; i < dim; ++i) {
+ bb[2 * i] = std::numeric_limits::lowest(); // min values
+ bb[2 * i + 1] = std::numeric_limits::max(); // max values
+ }
+ }
+ interface.setMeshAccessRegion(meshName, bb);
+ return coordinates;
+}
+
std::vector aste::setupMesh(precice::Participant &interface, const aste::Mesh &mesh, const std::string &meshName)
{
auto tstart = std::chrono::steady_clock::now();
diff --git a/src/utilities.hpp b/src/utilities.hpp
index f2578f55..23389b2d 100644
--- a/src/utilities.hpp
+++ b/src/utilities.hpp
@@ -60,4 +60,6 @@ void setupEdgeIDs(precice::Participant &interface, const aste::Mesh &mesh, const
* @return std::vector a vector of vertexIDs in preCICE
*/
std::vector setupMesh(precice::Participant &interface, const aste::Mesh &mesh, const std::string &meshName);
+
+std::vector setupDirectMeshAccess(precice::Participant &interface, const aste::Mesh &mesh, const std::string &meshName, const aste::ExecutionContext &exec);
} // namespace aste
diff --git a/tools/mapping-tester/config-template.xml b/tools/mapping-tester/config-template.xml
index 4b64eb8a..d560c7e3 100644
--- a/tools/mapping-tester/config-template.xml
+++ b/tools/mapping-tester/config-template.xml
@@ -1,6 +1,6 @@
-
+
@@ -15,10 +15,12 @@
-
+
+ {% if mapping.get('batch-size') == "-1" %}
+ {% endif %}
@@ -41,13 +43,16 @@
+ {% if mapping.get('batch-size') == "-1" %}
-
+ {% endif %}
+
+
{% if mapping.constraint == "consistent" %}
-
+
-
+
{% if mapping.kind.startswith("rbf") %}
{% if mapping.executor %}
diff --git a/tools/mapping-tester/generate.py b/tools/mapping-tester/generate.py
index aeed34b8..eacc2c1a 100755
--- a/tools/mapping-tester/generate.py
+++ b/tools/mapping-tester/generate.py
@@ -55,6 +55,7 @@ def generateCases(setup):
"executoroptions": mapping.get(
"executor-options", ""
),
+ "batch-size": mapping.get("batch-size", "-1"),
},
"A": {
"ranks": ranksA,
@@ -158,9 +159,15 @@ def createRunScript(outdir: pathlib.Path, path: pathlib.Path, case):
path, walk_up=True
)
mapped_data_name = case["function"] + "(mapped)"
+
+ # Handle batch_size condition
+ batch_size_flag = ""
output = "--output mapped" if case["computeAccuracy"] else ""
- bcmd = f'env {time_command} -f %M -a -o memory-B.log precice-aste-run -v -a -p B --data "{mapped_data_name}" --mesh {bmeshLocation} {output} || kill 0 &'
+ if case["mapping"]["batch-size"] != "-1":
+ batch_size_flag = "--read-just-in-time {}".format(case["mapping"]["batch-size"])
+ # Generate runner script for participant B
+ bcmd = f'env {time_command} -f %M -a -o memory-B.log precice-aste-run -v -a -p B --data "{mapped_data_name}" --mesh {bmeshLocation} {output} {batch_size_flag} || kill 0 &'
if branks > 1:
bcmd = "mpirun -n {} $ASTE_B_MPIARGS {}".format(branks, bcmd)