# rom.js

[![npm version](https://badge.fury.io/js/@simzero%2From.svg)](https://badge.fury.io/js/@simzero%2From)
[![Docker](https://badgen.net/badge/icon/docker?icon=docker&label)](https://github.com/orgs/simzero-oss/packages/container/package/rom-js)
[![stability-beta](https://img.shields.io/badge/stability-beta-33bbff.svg)]()
[![Join Slack](https://img.shields.io/badge/Join%20us%20on-Slack-e01563.svg)](https://join.slack.com/t/cfd-xyz/shared_invite/zt-12uquswo6-FFVy95vRjfMF~~t8j~UBHA)
[![License: LGPL v3](https://img.shields.io/badge/License-LGPL_v3-blue.svg)](https://github.com/simzero-oss/rom-js/blob/main/LICENSE)

`rom.js` is a JavaScript port for solving the online stage of reduced-order model (ROM) and visualizing the outputs on the web. The current version uses [OpenFOAM](https://www.openfoam.com) and [ITHACA-FV](https://github.com/mathLab/ITHACA-FV) for generating the CFD snapshots and the ROM.


`rom.js` is used in [http://cfd.xyz](http://cfd.xyz), check the website and the code at [cfd.xyz GitHub](https://github.com/simzero-oss/cfd-xyz).


This is a beta version, please handle it with care. Further features, optimizations and fixes are expected.

## Usage

This module can be directly installed with:

```console
npm install --save @simzero/rom
```

Alternatively, the module can be built and tested following the instructions described in [Option 1](#option-1-building-and-testing-with-docker-as-the-only-requirement) and [Option 2](#option-2-building-and-testing-by-compiling-the-third-party-libraries).

A minimal usage example can be found in `tests/pitzDaily` using node. This uses data generated by the `steady` application. The `offline` folder contains examples for generating this data.

```javascript
import rom from '@simzero/rom'
import Papa from 'papaparse'
import fs from 'fs'

const rootPath = "../../surrogates/OF/incompressible/simpleFoam/pitzDaily/";
const outputFile = 'U_' + U + '_nu_' + nu + '.vtu';

const loadData = async (dataPath) => {
  const data = await readFile(rootPath + dataPath)
  const vector = dataToVector(data);
  return vector;
}

const readFile = async (filePath) => {
  const csvFile = fs.readFileSync(filePath)
  const csvData = csvFile.toString()
  return new Promise(resolve => {
    Papa.parse(csvData, {
      delimiter: " ",
      dynamicTyping: true,
      skipEmptyLines: true,
      header: false,
      complete: results => {
        resolve(results.data);
      }
    });
  });
};

const dataToVector = (data) => {
  const vecVec = new rom.VectorVector();
  let rows = 0;
  let cols = 0;
  data.forEach(row => {
    var vec = new rom.Vector();
    row.forEach(value => {
      vec.push_back(value)
      if (rows === 0)
        cols++;
    });
    vecVec.push_back(vec)
    rows++;
  })
  return [vecVec, rows, cols];
}

(async () => {

  await rom.ready

  // - Velocity and viscosity values for the online solution
  const U = 3.0; // - m/s
  const nu = 1e-05; // - m2/s

  // - Number of patches with non-homogeneous BCs
  const N_BC = 1;

  // - Loading ROM data (e.g. generated with the steady app)
  const P = await loadData('matrices/P_mat.txt');
  const M = await loadData('matrices/M_mat.txt');
  const K = await loadData('matrices/K_mat.txt');
  const B = await loadData('matrices/B_mat.txt');
  const modes = await loadData('EigenModes_U_mat.txt');
  const coeffL2 = await loadData('matrices/coeffL2_mat.txt');
  const mu = await loadData('par.txt');
  const grid_data = fs.readFileSync('pitzDaily.vtu')

  // - Defining variables sizes
  const Nphi_u = B[1];
  const Nphi_p = K[2];
  const Nphi_nut = coeffL2[1];

  // - Defining reducedSteady
  const reduced = new rom.reducedSteady(Nphi_u + Nphi_p, Nphi_u + Nphi_p);


  // - Setting up grid and ROM data
  reduced.readUnstructuredGrid(grid_data);
  reduced.Nphi_u(Nphi_u);
  reduced.Nphi_p(Nphi_p);
  reduced.Nphi_nut(Nphi_nut);
  reduced.N_BC(N_BC);
  reduced.addMatrices(P, M, K, B);
  reduced.addModes(modes);
  reduced.setRBF(mu, coeffL2);

  // - Loading turbulent related matrices and weights
  for (var i = 0; i < Nphi_u; i ++ ){
    const C = await loadData("matrices/C" + i + "_mat.txt");
    const Ct1 = await loadData("matrices/ct1_" + i + "_mat.txt");
    const Ct2 = await loadData("matrices/ct2_" + i + "_mat.txt");

    reduced.addCMatrix(C, i);
    reduced.addCt1Matrix(Ct1, i);
    reduced.addCt2Matrix(Ct2, i);
  }

  for (var i = 0; i < Nphi_nut; i ++ ){
    const weight = await loadData('wRBF_' + i + '_mat.txt');
    reduced.addWeight(weight[0], i);
  }

  reduced.preprocess();

  // - Defining viscosity and solving for the given velocity
  reduced.nu(nu*1e-05);
  reduced.solveOnline(U);

  // - Reconstructing data from Eigen to VTK and exporting the grid
  reduced.reconstruct();
  const dataString = reduced.exportUnstructuredGrid();

  // - Saving the VTK file
  const outputFile = 'U_' + U + '_nu_' + nu + '.vtu';
  fs.writeFileSync(outputFile, dataString)

  reduced.delete();

})();
```

## Option 1 Building and testing with Docker as the only requirement 

This is the preferred and easiest method for working with this repository.

The [rom-js](https://github.com/orgs/simzero-oss/packages/container/package/rom-js) Docker image contains all the configuration and required third-party libraries for: 1) building the JavaScript module, 2) running OpenFOAM and 3) generating the ROM data using ITHACA-FV. The following versions are included in the image:

* [Emscripten](https://github.com/emscripten-core/emscripten) 3.1.1
* [npm](https://github.com/npm/cli) 8.5.3
* [node](https://github.com/nodejs/node) v14.18.2
* [Splinter](https://github.com/bgrimstad/splinter) v3.0 (compiled with emcc)
* [Eigen](https://gitlab.com/libeigen/eigen) v3.4.0 (compiled with emcc)
* [VTK](https://gitlab.kitware.com/vtk/vtk) v9.1 (compiled with emcc)
* [OpenFOAM](https://develop.openfoam.com/Development/openfoam) v2106
* [ITHACA-FV](https://github.com/mathLab/ITHACA-FV) 3.0 ([@carpemonf fork](https://github.com/carpemonf/ITHACA-FV))

### Requirements

* [Docker](https://www.docker.com/get-started) 20.10.12

### Building

The node version of the module can be executed outside a web browser with a back-end JavaScript runtime environment (Node.js). The following command builds the node version of the module:

```console
TARGET=node make all
```

This repository includes examples for solving the ROM online stage with the node version. A web version of the module can be also built and used in a web environment (check [cfd.xyz](http://cfd.xyz)). The following command builds the web version of the module:

```console
TARGET=web make all
```

### Generating the ROM data

The CFD snapshots are generated directly with OpenFOAM and later used in ITHACA-FV for generating the ROM. This results in hundreds of simulation executions and multiple GB of data. You can skip running this step by directly downloading the ROM data into the `surrogates` folder with:

```console
make data
```

Alternatively, You can also create the ROM data by yourself. Single core simulations are run in parallel in batches with the number of cores set by the `CORES` variable:

```console
CORES=8 make rom
```

The snasphots will be generated at `ITHACAoutput/Offline` with `ITHACAoutput/Parameters` showing the coressponding parameters for every folder in `ITHACAoutput/Offline`. The `ITHACAoutput/Reconstruction` contains folders for the online stage for the selected parameters. All the ROM data wil be collected in the `surrogates` folder.

### Using the ROM

Once that you have the surrogates you can cd into an example and test the `rom.js` module. For example:

```console
cd examples/OpenFOAM/incompressible/simpleFoam/pitzDaily
./run_online.sh
```

## Option 2 Building and testing by natively building the third-party libraries

This option compiles Emscripten versions of Splinter, Eigen and VTK. OpenFOAM and ITHACA-FV are also compiled.

### Requirements

* [Docker](https://www.docker.com/get-started) 20.10.12
* npm 6.14.11
* node v14.15.5

Note: Docker is only used for compiling with Emscripten.

### Building

Initialize the third-party submodules with:

```console
git submodule update --init --recursive
```

A local installation of ITHACA-FV ([@carpemonf fork](https://github.com/carpemonf/ITHACA-FV)) and OpenFOAM (v2106) is needed. You can use your local versions and skip this step and jump to [Generating the ROM data](#genereating-the-rom-data).

Check system [requirements](https://www.openfoam.com/documentation/system-requirements) for OpenFOAM and ITHACA-FV and run:


```console
make native-thirdparty
```

For building the node version of the `rom.js` module run:

```console
TARGET=node make native-thirdparty-emcc
```

For building the web version of the `rom.js` module run:

```console
TARGET=web make native-thirdparty-emcc
```

### Generating the ROM data

The CFD snapshots are generated directly with OpenFOAM and later used in ITHACA-FV for generating the ROM. This results in hundreds of simulation executions and multiple GB of data. You can skip running this step by directly downloading the ROM data into the `surrogates` folder with:

```console
make data
```

Alternatively, You can also create the ROM data by yourself. Single core simulations are run in parallel in batches with the number of cores set by the `CORES` variable:

```console
CORES=8 make native-rom
```

If you have your own installation of ITHACA-FV ([@carpemonf fork](https://github.com/carpemonf/ITHACA-FV)) and OpenFOAM (v2106), just set up the environment and run:

```console
cd examples && ./Allrun 8
```

The snasphots will be generated at `ITHACAoutput/Offline` with `ITHACAoutput/Parameters` showing the coressponding parameters for every folder in `ITHACAoutput/Offline`. The `ITHACAoutput/Reconstruction` contains folders for the online stage for the selected parameters. All the ROM data wil be collected in the `surrogates` folder.

### Using the ROM

Once that you have the surrogates you can cd into an example and test the `rom.js` module. For example:

```console
cd examples/OpenFOAM/incompressible/simpleFoam/pitzDaily
./run_online.sh
```

## Contributing

Everyone is welcome to contribute to this project. See [CONTRIBUTING.md](https://github.com/simzero-oss/rom-js/blob/main/CONTRIBUTING.md) for further details.

Your donations encourage future developments and help with server costs for providing a better service. If you like the tool you can [:heart: donate](https://www.paypal.com/donate/?hosted_button_id=KKB4LH96E59A4).

## Citation

A technical note related to this work was submitted to the OpenFOAM Journal and it is currently being reviewed. In the meanwhile please use the following bibtex to cite this repository:

- Technical note submitted to the OpenFOAM Journal and under review. Preprint available at arXiv:

```
@misc{cfd_xyz_note_2022,
  doi = {10.48550/ARXIV.2205.08387},
  url = {https://arxiv.org/abs/2205.08387},
  author = {Peña-Monferrer, Carlos and Díaz-Marín, Carmen},
  keywords = {Fluid Dynamics (physics.flu-dyn), Numerical Analysis (math.NA), FOS: Physical sciences, FOS: Physical sciences, FOS: Mathematics, FOS: Mathematics},
  title = {cfd.xyz/rom.js: An open-source framework for generating and visualizing parametric CFD results on the web},
  publisher = {arXiv},
  year = {2022},
  copyright = {Creative Commons Attribution Share Alike 4.0 International}
}
```

- Repository:
```
@online{rom_js_repo_2022,
  author       = {{SIMZERO}},
  title = {{rom.js}},
  howpublished = {\url{https://github.com/simzero-oss/rom-js}},
  addendum = "(accessed: 28.04.2022)",
}
```

## License

[GNU Lesser General Public License v3.0](https://github.com/simzero-oss/rom-js/blob/main/LICENSE)
