├── html
├── js
│ ├── index.html
│ └── faster-than-quick
├── img
│ └── logo.svg
├── new
│ ├── img
│ │ └── logo.svg
│ ├── index.php
│ ├── preview.php
│ ├── problems.php
│ ├── process.php
│ ├── import_cad.php
│ └── create.php
├── css
│ └── faster-than-quick
├── assets
│ └── named_cube.x3d
├── .htaccess
├── cad.php
├── mesh_log.php
├── expert.php
├── index.php
├── mesh.php
├── meshing.php
├── problem.php
├── results.php
├── solving.php
├── change_step.php
├── properties.php
├── ajax2mesh.php
├── mesh_data.php
├── ajax2problem.php
├── mesh_graph.php
├── problem_fee.php
├── results_data.php
├── mesh_inp_save.php
├── mesh_inp_show.php
├── meshing_status.php
├── solving_status.php
├── meshing_relaunch.php
├── problem_fee_save.php
├── solving_relaunch.php
├── mesh_msh.php
├── results_vtk.php
├── meshing_cancel.php
├── solving_cancel.php
├── ajax2yaml.php
├── common.php
└── case.php
├── .gitignore
├── data
└── .gitignore
├── meshers
├── gmsh
│ ├── LICENSE
│ ├── default0.geo
│ ├── .gitignore
│ ├── default1.geo
│ ├── default2.geo
│ ├── default3.geo
│ ├── default5.geo
│ ├── default4.geo
│ ├── quality.gp
│ ├── meshing_relaunch.php
│ ├── deps.sh
│ ├── mesh_log.php
│ ├── mesh_inp_show.php
│ ├── mesh_graph.php
│ ├── mesh_data.php
│ ├── mesh_inp_save.php
│ ├── quality.py
│ ├── cadmesh.py
│ ├── process_step.php
│ ├── mesh_status.sh
│ ├── meshing_status.php
│ ├── mesh.sh
│ ├── trymesh.py
│ ├── ajax2mesh.php
│ └── mesh_data.py
└── README.md
├── solvers
├── ccx
│ ├── LICENSE
│ ├── problem_fee.php
│ ├── ajax2problem.php
│ ├── results_data.php
│ ├── problem_fee_save.php
│ ├── input_initial_mechanical.php
│ ├── problems.php
│ ├── deps.sh
│ ├── change_step_solve.php
│ ├── frd2vtk.fee
│ ├── solve_status.sh
│ ├── common.php
│ ├── solving_status.php
│ └── solve.sh
├── feenox
│ ├── LICENSE
│ ├── field1.fee
│ ├── second2first.fee
│ ├── input_initial_heat_conduction.php
│ ├── field.fee
│ ├── input_initial_mechanical.php
│ ├── results_data.php
│ ├── deps.sh
│ ├── change_step_solve.php
│ ├── solving_relaunch.php
│ ├── problems.php
│ ├── problem_fee.php
│ ├── results_data
│ │ ├── heat_conduction.php
│ │ └── mechanical.php
│ ├── displacements.fee
│ ├── problem_fee_save.php
│ ├── solve_status.sh
│ ├── solving_status.php
│ ├── common.php
│ └── solve.sh
├── sparselizard
│ ├── LICENSE
│ └── problems.php
├── README.md
├── common.php
└── problems.php
├── uxs
├── .gitignore
└── faster-than-quick
│ ├── css
│ ├── index.html
│ ├── .gitignore
│ └── ftq.css
│ ├── js
│ ├── index.html
│ ├── .gitignore
│ ├── heat_conduction.js
│ └── mechanical.js
│ ├── bootswatch
│ ├── .gitignore
│ ├── make.sh
│ ├── _variables.scss
│ └── _bootswatch.scss
│ ├── labels
│ ├── .gitignore
│ ├── labels.sh
│ ├── README.md
│ ├── labels.awk
│ └── labels.txt
│ ├── results
│ ├── heat_conduction.php
│ └── mechanical.php
│ ├── mesh.php
│ ├── problem.php
│ ├── preview.php
│ ├── about.php
│ ├── cad.php
│ ├── share.php
│ ├── solving.php
│ ├── expert.php
│ ├── meshing.php
│ ├── deps.sh
│ ├── change_step.php
│ ├── importers
│ └── upload.php
│ ├── properties.php
│ ├── small_axes.html
│ └── results.php
├── .dockerignore
├── renderers
└── x3dom
│ ├── LICENSE
│ ├── .gitignore
│ └── deps.sh
├── auths
├── htpasswd
│ ├── README.md
│ ├── .htpasswd
│ ├── .htaccess
│ └── auth.php
└── single-user
│ └── auth.php
├── bin
└── .gitignore
├── cadprocessors
└── gmsh
│ ├── initial_mesh.sh
│ ├── cadcheck.py
│ ├── cad2stl.py
│ └── process.php
├── cadimporters
└── upload
│ ├── gmshcheck.py
│ └── import_cad.php
├── doc
├── README.md
├── virtual.md
├── workflow.md
├── FAQs.md
├── design.md
├── logo.svg
└── INSTALL.md
├── Dockerfile
├── conf.php
├── deps.sh
├── LICENSES.md
└── TODO.md
/html/js/index.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | deps
2 |
--------------------------------------------------------------------------------
/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 |
--------------------------------------------------------------------------------
/meshers/gmsh/LICENSE:
--------------------------------------------------------------------------------
1 | GPLv2+
--------------------------------------------------------------------------------
/meshers/gmsh/default0.geo:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/solvers/ccx/LICENSE:
--------------------------------------------------------------------------------
1 | GPLv2+
--------------------------------------------------------------------------------
/solvers/feenox/LICENSE:
--------------------------------------------------------------------------------
1 | GPLv3+
--------------------------------------------------------------------------------
/uxs/.gitignore:
--------------------------------------------------------------------------------
1 | caeplex
2 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 |
--------------------------------------------------------------------------------
/renderers/x3dom/LICENSE:
--------------------------------------------------------------------------------
1 | MIT/GPLv3+
--------------------------------------------------------------------------------
/html/img/logo.svg:
--------------------------------------------------------------------------------
1 | ../../doc/logo.svg
--------------------------------------------------------------------------------
/solvers/sparselizard/LICENSE:
--------------------------------------------------------------------------------
1 | GPLv2+
--------------------------------------------------------------------------------
/uxs/faster-than-quick/css/index.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/js/index.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/html/new/img/logo.svg:
--------------------------------------------------------------------------------
1 | ../../img/logo.svg
--------------------------------------------------------------------------------
/renderers/x3dom/.gitignore:
--------------------------------------------------------------------------------
1 | x3dom.css
2 | x3dom.js
3 |
--------------------------------------------------------------------------------
/solvers/ccx/problem_fee.php:
--------------------------------------------------------------------------------
1 | ../feenox/problem_fee.php
--------------------------------------------------------------------------------
/html/css/faster-than-quick:
--------------------------------------------------------------------------------
1 | ../../uxs/faster-than-quick/css/
--------------------------------------------------------------------------------
/html/js/faster-than-quick:
--------------------------------------------------------------------------------
1 | ../../uxs/faster-than-quick/js
--------------------------------------------------------------------------------
/solvers/ccx/ajax2problem.php:
--------------------------------------------------------------------------------
1 | ../feenox/ajax2problem.php
--------------------------------------------------------------------------------
/solvers/ccx/results_data.php:
--------------------------------------------------------------------------------
1 | ../feenox/results_data.php
--------------------------------------------------------------------------------
/auths/htpasswd/README.md:
--------------------------------------------------------------------------------
1 | Explain how to setup httpasswd.
2 |
--------------------------------------------------------------------------------
/meshers/gmsh/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | gmsh*
3 | libgmsh*
4 |
--------------------------------------------------------------------------------
/meshers/gmsh/default1.geo:
--------------------------------------------------------------------------------
1 | Mesh.MeshSizeFromCurvature = 5;
2 |
--------------------------------------------------------------------------------
/solvers/ccx/problem_fee_save.php:
--------------------------------------------------------------------------------
1 | ../feenox/problem_fee_save.php
--------------------------------------------------------------------------------
/html/assets/named_cube.x3d:
--------------------------------------------------------------------------------
1 | ../../uxs/faster-than-quick/named_cube.x3d
--------------------------------------------------------------------------------
/uxs/faster-than-quick/js/.gitignore:
--------------------------------------------------------------------------------
1 | bootstrap*.js
2 | x3dom*.js
3 |
--------------------------------------------------------------------------------
/auths/htpasswd/.htpasswd:
--------------------------------------------------------------------------------
1 | pepe:$apr1$vATa7bcq$EBEVaET9ke0EWE6zs4P4Q1
2 |
--------------------------------------------------------------------------------
/meshers/README.md:
--------------------------------------------------------------------------------
1 | # Meshers
2 |
3 | * [Gmsh](http://gmsh.info/) (GPLv2+)
4 |
--------------------------------------------------------------------------------
/solvers/ccx/input_initial_mechanical.php:
--------------------------------------------------------------------------------
1 | ../feenox/input_initial_mechanical.php
--------------------------------------------------------------------------------
/meshers/gmsh/default2.geo:
--------------------------------------------------------------------------------
1 | Mesh.MeshSizeFromCurvature = 10;
2 | Mesh.Algorithm = 1;
3 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/bootswatch/.gitignore:
--------------------------------------------------------------------------------
1 | bootswatch
2 | package-lock.json
3 |
4 |
--------------------------------------------------------------------------------
/bin/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | feenox
3 | fee2ccx
4 | ccx
5 | gmsh*
6 | libgmsh*
7 | pandoc*
8 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/labels/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package.json
3 | package-lock.json
4 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/labels/labels.sh:
--------------------------------------------------------------------------------
1 | awk -f labels.awk labels.txt | nodejs > ../labels.php
2 |
--------------------------------------------------------------------------------
/auths/htpasswd/.htaccess:
--------------------------------------------------------------------------------
1 | AuthType Basic
2 | AuthName "Restricted Content"
3 | AuthUserFile .htpasswd
4 | Require valid-user
5 |
--------------------------------------------------------------------------------
/meshers/gmsh/default3.geo:
--------------------------------------------------------------------------------
1 | Mesh.MeshSizeFromCurvature = 12;
2 | Mesh.AlgorithmSwitchOnFailure = 1;
3 | Mesh.Algorithm = 2;
4 |
--------------------------------------------------------------------------------
/cadprocessors/gmsh/initial_mesh.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ ! -e default.geo ]; then
4 | python ../../../../meshers/gmsh/cadmesh.py
5 | fi
6 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/css/.gitignore:
--------------------------------------------------------------------------------
1 | bootstrap-icons.min.css
2 | bootstrap-icons.css
3 | x3dom.css
4 | fonts
5 | katex.min.css
6 | katex.css
7 |
--------------------------------------------------------------------------------
/cadimporters/upload/gmshcheck.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append("../../../../bin")
4 | import gmsh
5 |
6 | gmsh.initialize()
7 | gmsh.finalize()
8 |
--------------------------------------------------------------------------------
/html/.htaccess:
--------------------------------------------------------------------------------
1 | # AuthType Basic
2 | # AuthName "Restricted Content"
3 | # AuthUserFile /home/gtheler/codigos/suncae/auths/htpasswd/.htpasswd
4 | # Require valid-user
5 |
--------------------------------------------------------------------------------
/doc/README.md:
--------------------------------------------------------------------------------
1 | # SunCAE documentation
2 |
3 | * [Installation guide](INSTALL.md)
4 | * [Tutorials](tutorials.md)
5 | * [FAQs](FAQs.md)
6 | * [Explanation of the workflow](workflow.md)
7 |
--------------------------------------------------------------------------------
/meshers/gmsh/default5.geo:
--------------------------------------------------------------------------------
1 | Mesh.MeshSizeFromCurvature = 20;
2 | Mesh.MeshSizeExtendFromBoundary = 1;
3 | Mesh.MinimumLineNodes = 8;
4 | Mesh.Algorithm = 2;
5 | Mesh.MeshSizeFactor = 0.5;
6 |
--------------------------------------------------------------------------------
/meshers/gmsh/default4.geo:
--------------------------------------------------------------------------------
1 | Mesh.MeshSizeFromCurvature = 16;
2 | Mesh.MeshSizeExtendFromBoundary = 1;
3 | Mesh.AlgorithmSwitchOnFailure = 1;
4 | Mesh.MinimumLineNodes = 8;
5 | Mesh.Algorithm = 2;
6 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/labels/README.md:
--------------------------------------------------------------------------------
1 | Edit `labels.txt` and run
2 |
3 | ```
4 | ./labels.sh
5 | ```
6 |
7 | Rembember to install KaTeX:
8 |
9 | ```
10 | npm install katex
11 | ```
12 |
13 |
--------------------------------------------------------------------------------
/doc/virtual.md:
--------------------------------------------------------------------------------
1 | # Setting up virtual machines to host SunCAE
2 |
3 | ## Vagrant
4 |
5 | ```terminal
6 | vagrant init debian/bookworm64
7 | vagrant up
8 | vagrant ssh
9 | ```
10 |
11 | ## Docker
12 |
13 | TBD
14 |
--------------------------------------------------------------------------------
/meshers/gmsh/quality.gp:
--------------------------------------------------------------------------------
1 | set style fill transparent solid 0.50 noborder
2 | set style function filledcurves y1=0
3 | plot "data" u (($1+0.5)/10):3 with boxes, "data" u (($1+0.5)/10):4 with boxes, "data" u (($1+0.5)/10):2 with boxes
4 |
--------------------------------------------------------------------------------
/solvers/README.md:
--------------------------------------------------------------------------------
1 | # Solvers
2 |
3 | * [FeenoX](https://www.seamplex.com/feenox) (GPLv3+)
4 | * [CalculiX](http://calculix.de/) (GPLv3+)---through the [`fee2ccx`](https://github.com/seamplex/feenox/tree/main/utils/fee2ccx) converter
5 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/labels/labels.awk:
--------------------------------------------------------------------------------
1 | BEGIN {
2 | print "const katex = require(\"katex\");"
3 | print "console.log(\" bash
14 |
--------------------------------------------------------------------------------
/solvers/feenox/input_initial_heat_conduction.php:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/solvers/feenox/input_initial_mechanical.php:
--------------------------------------------------------------------------------
1 | run/{$problem_hash}-check.2", $output, $result);
9 | if ($result == 0) {
10 | exec("../../../../solvers/ccx/solve.sh {$problem} > run/{$problem_hash}-solve.log 2>&1 & echo $! > run/solving.pid");
11 | $results_meta["status"] = "running";
12 | suncae_log("{$id} problem running");
13 | } else {
14 | $results_meta["status"] = "syntax_error";
15 | suncae_log("{$id} problem syntax error");
16 | }
17 |
18 | ?>
19 |
--------------------------------------------------------------------------------
/solvers/feenox/change_step_solve.php:
--------------------------------------------------------------------------------
1 | run/{$problem_hash}-check.2", $output, $result);
9 | if ($result == 0) {
10 | exec("../../../../solvers/feenox/solve.sh {$problem} > run/{$problem_hash}-solve.log 2>&1 & echo $! > run/solving.pid");
11 | $results_meta["status"] = "running";
12 | suncae_log("{$id} problem running");
13 | } else {
14 | $results_meta["status"] = "syntax_error";
15 | suncae_log("{$id} problem syntax error");
16 | }
17 |
18 | ?>
19 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/js/heat_conduction.js:
--------------------------------------------------------------------------------
1 | color["bc_1"] = [0.60, 0.30, 0.90];
2 | color["bc_2"] = [1.00, 0.33, 0.33];
3 | color["bc_3"] = [0.67, 0.77, 0.37];
4 | color["bc_4"] = [0.16, 0.83, 1.00];
5 | color["bc_5"] = [0.40, 1.00, 0.40];
6 | color["bc_6"] = [1.00, 0.90, 0.50];
7 | color["bc_7"] = [0.40, 0.80, 0.85];
8 | color["bc_8"] = [1.00, 0.00, 0.40];
9 | color["bc_9"] = [0.16, 0.83, 0.00];
10 | color["bc_10"] = [1.00, 0.40, 0.00];
11 |
12 | function bc_hide_all(i) {
13 | bootstrap_hide("bc_value_"+i+"_custom");
14 | bootstrap_hide("bc_value_"+i+"_temperature");
15 | bootstrap_hide("bc_value_"+i+"_heatflux");
16 | bootstrap_hide("bc_value_"+i+"_convection");
17 | }
18 |
--------------------------------------------------------------------------------
/html/mesh_log.php:
--------------------------------------------------------------------------------
1 | /'");
8 | $response["plain"] = shell_exec("tail -n+2 case.fee");
9 | $response["html"] = shell_exec("cat << EOF | ../../../../bin/pandoc -t html --syntax-definition=../../../../solvers/feenox/feenox.xml
10 | ~~~feenox
11 | $(cat case.fee)
12 | ~~~
13 | EOF");
14 |
15 | return_back_json($response);
16 |
--------------------------------------------------------------------------------
/solvers/feenox/results_data/heat_conduction.php:
--------------------------------------------------------------------------------
1 | 0, 0.1 * ${2} / displ_max, 1)
11 |
12 | sigma_max = vecmax(vec_sigma)
13 |
14 | OUTPUT_FILE max PATH ${1}-max.json
15 | # TODO: implement PRINTF with FILE
16 | PRINT FILE max "\{"
17 | PRINT FILE max " \"max_displacement\": " displ_max ","
18 | PRINT FILE max " \"max_warp\":" warp_max ","
19 | PRINT FILE max " \"max_sigma\":" sigma_max
20 | PRINT FILE max "\}"
21 |
22 | VECTOR dxv[nodes]
23 | VECTOR dyv[nodes]
24 | VECTOR dzv[nodes]
25 |
26 | dxv[i] = vec_u_x[i] + warp_max*vec_u[i]
27 | dyv[i] = vec_u_y[i] + warp_max*vec_v[i]
28 | dzv[i] = vec_u_z[i] + warp_max*vec_w[i]
29 |
30 | PRINT_VECTOR dxv dyv dzv
31 |
32 |
33 |
--------------------------------------------------------------------------------
/meshers/gmsh/mesh_graph.php:
--------------------------------------------------------------------------------
1 |
31 |
--------------------------------------------------------------------------------
/meshers/gmsh/mesh_inp_save.php:
--------------------------------------------------------------------------------
1 | &1", $output, $result);
17 | if ($result != 0) {
18 | $response["status"] = "error";
19 | for ($i = 0; $i < count($output); $i++) {
20 | if (strncmp("Error", $output[$i], 5) == 0) {
21 | $response["error"] .= $output[$i] . " ";
22 | }
23 | }
24 | }
25 |
26 | return_back_json($response);
27 |
--------------------------------------------------------------------------------
/renderers/x3dom/deps.sh:
--------------------------------------------------------------------------------
1 | #!/bin/false
2 |
3 | # x3dom_version=1.8.3
4 | x3dom_version=1.8.4-dev
5 |
6 | echo -n "renderers/x3dom: x3dom.js & x3dom.css... "
7 | if [ $force = 1 ] || [ ! -e renderers/x3dom/x3dom.js ]; then
8 | cd deps
9 | x3dom_tarball=x3dom-${x3dom_version}
10 |
11 | # if [ ! -e ${x3dom_tarball}.zip ]; then
12 | # wget -c https://www.x3dom.org/download/${x3dom_version}/${x3dom_tarball}.zip
13 | # wget -c https://www.x3dom.org/download/dev/${x3dom_tarball}.zip
14 | # fi
15 | # if [ ! -d x3dom ]; then
16 | # mkdir -p x3dom
17 | # unzip ${x3dom_tarball}.zip -d x3dom
18 | # fi
19 | # cp x3dom/x3dom.js ../renderers/x3dom
20 | # cp x3dom/x3dom.css ../renderers/x3dom
21 |
22 | wget -c https://andreasplesch.github.io/x3dom/dist/x3dom.js
23 | cp x3dom.js ../renderers/x3dom
24 | wget -c https://andreasplesch.github.io/x3dom/dist/x3dom.css
25 | cp x3dom.css ../renderers/x3dom
26 |
27 | cd ../uxs/faster-than-quick/js
28 | if [ ! -e x3dom.js ]; then
29 | ln -s ../../../renderers/x3dom/x3dom.js
30 | fi
31 | cd ../css
32 | if [ ! -e x3dom.css ]; then
33 | ln -s ../../../renderers/x3dom/x3dom.css
34 | fi
35 | echo "done"
36 | cd ../../..
37 | else
38 | echo "already installed"
39 | fi
40 |
--------------------------------------------------------------------------------
/solvers/feenox/results_data/mechanical.php:
--------------------------------------------------------------------------------
1 |
5 |
6 | | Type | Name | Repository | License |
7 | |:----------------|:--------------|:-------------------------------------------|:-------------:|
8 | | Renderer | X3DOM | | MIT/GPLv3+ |
9 | | UX | Bootstrap | | MIT |
10 | | CAD importer | OpenCASCADE | | LGPL-2.1 |
11 | | Mesher | Gmsh | | GPLv2+ |
12 | | Solver | FeenoX | | GPLv3+ |
13 | | Solver | CalculiX | N/A | GPLv2+ |
14 |
15 | Other dependencies (i.e. new solvers, new meshers) can be added as long as their respective licenses are respected.
16 |
17 |
--------------------------------------------------------------------------------
/doc/workflow.md:
--------------------------------------------------------------------------------
1 | # New case
2 |
3 | #. if someone goes to the root `index.php` (at `html/index.php`) without and `id` or directly to `new/` then the "new case" page at `new/index.php` is shown
4 | #. that one includes `uxs/$ux/new.php`
5 |
6 | ## Faster-than-quick UX
7 |
8 | #. `uxs/faster-than-quick/new.php` shows the four choices
9 |
10 | 2. Physics
11 | 3. Problem
12 | 4. Solver
13 | 5. Mesher
14 |
15 | But the first one, which is the CAD, comes from `uxs/faster-than-quick/importers/$cadimporter.php`
16 |
17 | ## Upload-importer
18 |
19 | #. `uxs/faster-than-quick/importers/upload.php` shows a file upload HTML component with an onchange call to `upload_cad_file()` that after uploading the CAD, calls `process_cad()`.
20 | #. `process_cad()` in `uxs/faster-than-quick/new.php` calls `html/new/process.php` which includes `cadprocessors/$cadprocessor/process.php`
21 |
22 | ## Gmsh-processor
23 |
24 | #. `cadprocessors/gmsh/process.php` uses `cadimport.py` to process the uploaded CAD with Gmsh (through OCC) to create a `.xao`
25 | #. It calls `initial_mesh.sh` in background to create the first mesh.
26 | #. This `initial_mesh.sh` just calls `cadmesh.py` if `default.geo` does not exit.
27 | #. `cadmesh.py` performs five attempts to create an initial mesh by calling `trymesh.py` with the number of attempt $i$ as the argument
28 | #. `trymesh.py` merges `defaulti.geo` and then tries to come up with a max $\ell_c$
29 | #.
30 |
31 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/bootswatch/make.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -ex
2 |
3 | # https://bootswatch.com/help/#customization
4 |
5 | # * Download the repository: git clone https://github.com/thomaspark/bootswatch.git
6 | # * Install dependencies: npm install
7 | # * Make sure that you have grunt available in the command line. You can install grunt-cli as described on the Grunt Getting Started page.
8 | # * In /dist, modify _variables.scss and _bootswatch.scss in one of the theme directories, or duplicate a theme directory to create a new one.
9 | # * Type grunt swatch:[theme] to build the CSS for a theme, e.g., grunt swatch:flatly for Flatly. Or type grunt swatch to build them all at once.
10 | # * You can run grunt to start a server, watch for any changes to the SASS files, and automatically build a theme and reload it on change. Run grunt server for just the server, and grunt watch for just the watcher.
11 |
12 | if [ ! -d bootswatch ]; then
13 | git clone https://github.com/thomaspark/bootswatch.git
14 | cd bootswatch
15 | else
16 | cd bootswatch
17 | git pull
18 | fi
19 |
20 | if [ -z "$(which npm)" ]; then
21 | sudo apt-get install npm
22 | fi
23 | if [ -z "$(which grunt)" ]; then
24 | sudo apt-get install grunt
25 | fi
26 |
27 | npm install
28 | # npm audit fix
29 | mkdir -p dist/faster-than-quick
30 | cp ../_bootswatch.scss ../_variables.scss dist/faster-than-quick/
31 | grunt swatch:faster-than-quick
32 | cp dist/faster-than-quick/bootstrap.min.css ../../css/
33 |
34 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/about.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | A free and open source web-based platform for performing CAE in the cloud.
16 |
17 |
18 |
Versions
19 |
20 | ";
23 | echo "Gmsh ".`../bin/gmsh -info | head -n1`;
24 | ?>
25 |
26 |
License
27 |
28 |
GNU Affero General Public License version 3, or at your option, any later version.
29 | You can get a copy of the source code of this web interface
here .
30 |
31 |
32 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/js/mechanical.js:
--------------------------------------------------------------------------------
1 | color["bc_1"] = [0.82, 0.22, 0.68];
2 | color["bc_2"] = [0.37, 0.85, 0.16];
3 | color["bc_3"] = [0.93, 0.91, 0.26];
4 | color["bc_4"] = [0.33, 0.86, 1.00];
5 | color["bc_5"] = [1.00, 0.66, 0.66];
6 | color["bc_6"] = [1.00, 0.50, 0.50];
7 | color["bc_7"] = [0.00, 0.80, 1.00];
8 | color["bc_8"] = [0.55, 0.37, 0.82];
9 | color["bc_9"] = [0.00, 1.00, 0.80];
10 | color["bc_10"] = [1.00, 0.40, 0.00];
11 |
12 | color["bc_10"] = [0.10, 0.25, 0.50];
13 | color["bc_11"] = [1.00, 0.80, 0.16];
14 | color["bc_12"] = [0.75, 0.15, 0.90];
15 | color["bc_13"] = [0.55, 0.01, 0.22];
16 | color["bc_14"] = [0.17, 0.38, 0.01];
17 | color["bc_15"] = [0.78, 0.44, 0.21];
18 | color["bc_16"] = [1.00, 0.25, 0.50];
19 | color["bc_17"] = [0.25, 0.83, 0.60];
20 | color["bc_18"] = [0.35, 0.75, 0.60];
21 | color["bc_19"] = [0.21, 0.78, 0.78];
22 |
23 | function bc_hide_all(i) {
24 | bootstrap_hide("bc_value_"+i+"_custom");
25 | bootstrap_hide("bc_value_"+i+"_fixture");
26 | bootstrap_hide("bc_value_"+i+"_displacement");
27 | bootstrap_hide("bc_value_"+i+"_pressure");
28 | bootstrap_hide("bc_value_"+i+"_force");
29 | }
30 |
31 | function bc_fixture_update(i) {
32 | string = "";
33 | if (document.getElementById("bc_"+i+"_fixture_u").checked) {
34 | string += "u=0 ";
35 | }
36 | if (document.getElementById("bc_"+i+"_fixture_v").checked) {
37 | string += "v=0 ";
38 | }
39 | if (document.getElementById("bc_"+i+"_fixture_w").checked) {
40 | string += "w=0 ";
41 | }
42 |
43 | ajax2problem("bc_"+i+"_value" , string);
44 | }
45 |
--------------------------------------------------------------------------------
/cadprocessors/gmsh/cadcheck.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append("../../../../bin")
4 | import gmsh
5 | import os
6 | import math
7 | import json
8 |
9 | def main():
10 | gmsh.initialize()
11 | gmsh.option.setNumber("General.Terminal", 0)
12 |
13 | try:
14 | gmsh.open("original.step")
15 | except:
16 | print("invalid STEP");
17 | sys.exit(1)
18 |
19 | try:
20 | [xmin, ymin, zmin, xmax, ymax, zmax] = gmsh.model.getBoundingBox(-1, -1)
21 |
22 | except:
23 | print("invalid CAD");
24 | sys.exit(2)
25 |
26 | orig = {}
27 | orig["xmin"] = xmin
28 | orig["xmax"] = xmax
29 | orig["ymin"] = ymin
30 | orig["ymax"] = ymax
31 | orig["zmin"] = zmin
32 | orig["zmax"] = zmax
33 | orig["max_length"] = math.sqrt(math.pow(xmax-xmin,2) + math.pow(ymax-ymin,2) + math.pow(zmax-zmin,2))
34 | orig["max_delta"] = max(xmax-xmin, ymax-ymin, zmax-zmin)
35 | # orig["tolerance"] = 7e-5*orig["max_length"] if options.ext != "brep" else 0
36 |
37 | orig["solids"] = len(gmsh.model.getEntities(3))
38 | orig["faces"] = len(gmsh.model.getEntities(2))
39 | orig["edges"] = len(gmsh.model.getEntities(1))
40 | orig["vertices"] = len(gmsh.model.getEntities(0))
41 |
42 | # orig["format"] = options.ext
43 | orig["format"] = "step"
44 | orig["size"] = os.path.getsize("original.step")
45 |
46 |
47 |
48 | with open("original.json", "w") as fp:
49 | json.dump(orig, fp, indent=2)
50 |
51 |
52 | gmsh.finalize()
53 | sys.exit(0);
54 |
55 |
56 | if __name__ == "__main__":
57 | main()
58 |
59 |
--------------------------------------------------------------------------------
/meshers/gmsh/quality.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append("../../../../bin")
4 | import gmsh
5 | import math
6 |
7 | gmsh.initialize(sys.argv)
8 | gmsh.option.setNumber("General.Terminal", 0)
9 |
10 | gmsh.open("mesh.msh")
11 |
12 | ## get element qualities:
13 | #_, eleTags , _ = gmsh.model.mesh.getElements(dim=3)
14 | #q = gmsh.model.mesh.getElementQualities(eleTags[0], "minSICN")
15 | #print(zip(eleTags[0], q))
16 |
17 | gmsh.plugin.setNumber("AnalyseMeshQuality", "JacobianDeterminant", 1.)
18 | gmsh.plugin.setNumber("AnalyseMeshQuality", "IGEMeasure", 1.)
19 | gmsh.plugin.setNumber("AnalyseMeshQuality", "ICNMeasure", 1.)
20 |
21 | gmsh.plugin.setNumber("AnalyseMeshQuality", "CreateView", 1.)
22 | gmsh.plugin.run("AnalyseMeshQuality")
23 |
24 | dataType, tags, Jac, time, numComp = gmsh.view.getModelData(0, 0)
25 | dataType, tags, IGE, time, numComp = gmsh.view.getModelData(1, 0)
26 | dataType, tags, ICN, time, numComp = gmsh.view.getModelData(2, 0)
27 |
28 | N = 20
29 | hist_Jac = [0]*(N+1)
30 | hist_IGE = [0]*(N+1)
31 | hist_ICN = [0]*(N+1)
32 |
33 |
34 | for i in range(len(tags)):
35 | n_Jac = int(math.floor(Jac[i][0]*N))
36 | n_IGE = int(math.floor(IGE[i][0]*N))
37 | n_ICN = int(math.floor(ICN[i][0]*N))
38 | #print(n_Jac, n_IGE, n_ICN)
39 | hist_Jac[n_Jac] += 1
40 | hist_IGE[n_IGE] += 1
41 | hist_ICN[n_ICN] += 1
42 |
43 | # for qualities = 1 they go to N and we move it to N-1
44 | hist_Jac[N-1] += hist_Jac[N]
45 | hist_IGE[N-1] += hist_IGE[N]
46 | hist_ICN[N-1] += hist_ICN[N]
47 |
48 |
49 | for j in range(N):
50 | print("{0}\t{1}\t{2}\t{3}".format(j,hist_Jac[j],hist_IGE[j],hist_ICN[j]))
51 |
52 | gmsh.finalize()
53 |
--------------------------------------------------------------------------------
/html/meshing_cancel.php:
--------------------------------------------------------------------------------
1 | &1", $output, $result);
18 | if ($result != 0) {
19 | $response["status"] = "error";
20 | for ($i = 0; $i < count($output); $i++) {
21 | // this authorization comes from openmpi
22 | if ($output[$i] != "" && strncasecmp($output[$i], "Authorization", 13) != 0) {
23 | if (strncmp("error", $output[$i], 5) == 0) {
24 | $output_exploded = explode(":", $output[$i]);
25 | for ($j = 2; $j < count($output_exploded); $j++) {
26 | $response["error"] .= $output_exploded[$j] ;
27 | }
28 | $response["error"] .= " ";
29 | } else {
30 | $response["error"] .= $output[$i] . " ";
31 | }
32 | }
33 | }
34 | }
35 |
36 | return_back_json($response);
37 |
--------------------------------------------------------------------------------
/cadimporters/upload/import_cad.php:
--------------------------------------------------------------------------------
1 | attempt%d.log 2>&1" % (attempt, attempt))
9 | if os.path.exists("attempt%d.json" % attempt):
10 | fp = open("attempt%d.json" % attempt)
11 | result = json.load(fp)
12 | fp.close()
13 | return result["result"] == "success"
14 | else:
15 | return False
16 |
17 | def main():
18 |
19 | # write a basic default.geo and close it so it's available in case it's needed
20 | geo = open("default.geo", "w")
21 | geo.write("Merge \"../../cads/%s/cad.xao\";\n" % os.path.basename(os.path.normpath(os.getcwd())))
22 | geo.close()
23 |
24 | # try to obtain a valid mesh
25 | i = 0;
26 | i_max = 5;
27 | while mesh(i) == False:
28 | print("%d-th attempt failed" % (i))
29 | i += 1
30 | if i == i_max+1:
31 | print("max attempts reached")
32 | sys.exit(1)
33 |
34 | print("final success with attempt = %d" % i)
35 |
36 | # append what worked
37 | geo = open("default.geo", "a")
38 | if True:
39 | print(i)
40 | fp = open("attempt%d.json" % i)
41 | attempt = json.load(fp)
42 | fp.close()
43 |
44 | print(attempt)
45 | if attempt["lc"] != 0:
46 | lc = float(attempt["lc"])
47 | geo.write("Mesh.MeshSizeMax = %g;\n" % (lc));
48 | geo.write("Mesh.MeshSizeMin = %g;\n" % (1e-2*lc));
49 |
50 | default = open("../../../../meshers/gmsh/default%d.geo" % i)
51 | geo.write(default.read())
52 | default.close()
53 | geo.close()
54 |
55 | if os.path.exists("meshes") == False:
56 | os.system("../../../../meshers/gmsh/mesh.sh cad");
57 | sys.exit(0)
58 |
59 | if __name__ == "__main__":
60 | main()
61 |
--------------------------------------------------------------------------------
/meshers/gmsh/process_step.php:
--------------------------------------------------------------------------------
1 | 1) {
32 | $response["status"] = "error";
33 | $response["error"] = "CAD file has {$original["solids"]} solids and this PoC works with single-solid CADs only.";
34 | }
35 | } else {
36 | $response["status"] = "error";
37 | $response["show_preview"] = false;
38 | $response["error"] = "Cannot decode original json.";
39 | }
40 |
41 | } else {
42 | $response["status"] = "error";
43 | $response["show_preview"] = false;
44 | $response["error"] = "Cannot create original json.";
45 | }
46 |
47 | suncae_log("CAD {$response["cad_hash"]} upload {$response["status"]} {$response["error"]}");
48 |
49 | ?>
50 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/cad.php:
--------------------------------------------------------------------------------
1 | run/${problem_hash}-status.json
52 | {
53 | "status": "running",
54 | "pid": ${feenox_pid},
55 | "mesh": ${mesh},
56 | "build": ${build},
57 | "solve": ${solve},
58 | "post": ${post},
59 | "data": ${data},
60 | "done_mesh": ${done_mesh},
61 | "done_build": ${done_build},
62 | "done_solve": ${done_solve},
63 | "done_post": ${done_post},
64 | "done_data": ${done_data}
65 | }
66 | EOF
67 | # sync run/${problem_hash}.json
68 |
69 | else
70 | exit 0
71 | fi
72 |
--------------------------------------------------------------------------------
/solvers/feenox/solve_status.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ -z "${1}" ]; then
4 | problem_hash=($(md5sum case.fee))
5 | else
6 | problem_hash=${1}
7 | fi
8 |
9 | status=$(jq -r .status run/${problem_hash}.json) || exit 1
10 | if [ "x${status}" == "xrunning" ]; then
11 | feenox_pid=$(jq -r .pid run/${problem_hash}.json)
12 | echo $feenox_pid
13 | if [ -z "$(ps --no-headers --pid ${feenox_pid})" ]; then
14 | echo "solver pid ${feenox_pid} is not running"
15 | echo "TODO: check if it worked or not"
16 | exit 1
17 | fi
18 |
19 | mesh=0
20 | meshfile=$(grep MESH case.fee | awk '{print $4}' | head -n1)
21 | meshname=$(basename ${meshfile} .msh)
22 | if [ -e run/meshes/${meshname}.1 ]; then
23 | mesh=$(grep % run/meshes/${meshname}.1 | tr -d [] | awk '{print $3}' | tr -d % | tail -n1)
24 | fi
25 |
26 | logfile=run/${problem_hash}.1
27 | build=$(grep \\. ${logfile} | tr -d '\n' | wc -c)
28 | solve=$(grep \\- ${logfile} | tr -d '\n' | wc -c)
29 | post=$(grep = ${logfile} | tr -d '\n' | wc -c)
30 | data=50
31 |
32 | done_mesh=0
33 | if [ ${mesh} = 100 ]; then
34 | done_mesh=1
35 | fi
36 | done_build=0
37 | if [ ${build} = 100 ]; then
38 | done_build=1
39 | fi
40 | done_solve=0
41 | if [ ${solve} = 100 ]; then
42 | done_solve=1
43 | fi
44 | done_post=0
45 | if [ ${post} = 100 ]; then
46 | done_post=1
47 | fi
48 |
49 | done_data=0
50 |
51 | cat << EOF > run/${problem_hash}-status.json
52 | {
53 | "status": "running",
54 | "pid": ${feenox_pid},
55 | "mesh": ${mesh},
56 | "build": ${build},
57 | "solve": ${solve},
58 | "post": ${post},
59 | "data": ${data},
60 | "done_mesh": ${done_mesh},
61 | "done_build": ${done_build},
62 | "done_solve": ${done_solve},
63 | "done_post": ${done_post},
64 | "done_data": ${done_data}
65 | }
66 | EOF
67 | # sync run/${problem_hash}.json
68 |
69 | else
70 | exit 0
71 | fi
72 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/share.php:
--------------------------------------------------------------------------------
1 |
12 |
13 | Share case
14 |
15 |
16 |
17 |
18 |
23 |
24 |
25 | choose email address
26 |
27 |
28 |
29 |
30 |
35 |
36 |
37 | Twitter,
38 | LinkedIn,
39 | etc
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/meshers/gmsh/mesh_status.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ -z "${1}" ]; then
4 | mesh_hash=($(md5sum mesh.geo))
5 | else
6 | mesh_hash=${1}
7 | fi
8 |
9 | status=$(jq -r .status run/meshes/${mesh_hash}.json) || exit 1
10 | if [ "x${status}" == "xrunning" ]; then
11 | gmsh_pid=$(jq -r .pid run/meshes/${mesh_hash}.json)
12 | echo $gmsh_pid
13 | if [ -z "$(ps --no-headers --pid ${gmsh_pid})" ]; then
14 | echo "mesher pid ${gmsh_pid} is not running"
15 | echo "TODO: check if it worked or not"
16 | exit 1
17 | fi
18 |
19 | logfile=run/meshes/${mesh_hash}.1
20 | edges=$(grep "Meshing curve" ${logfile} | tail -n1 | tr -d '[]' | awk '{print $6}')
21 | if [ -z "${edges}" ]; then
22 | edges="0"
23 | fi
24 | faces=$(grep "Meshing surface" ${logfile} | tail -n1 | tr -d '[]' | awk '{print $6}')
25 | if [ -z "${faces}" ]; then
26 | faces="0"
27 | fi
28 | volumes=$(grep "Meshing 3D..." ${logfile} | wc -l)
29 |
30 | done_edges=$(grep 'Done meshing 1D' ${logfile} | wc -l)
31 | done_faces=$(grep 'Done meshing 2D' ${logfile} | wc -l)
32 | done_volumes=$(grep 'Done meshing 3D' ${logfile} | wc -l)
33 |
34 | data=0
35 | datalogfile=run/meshes/${mesh_hash}-data.log
36 | if [ -e ${datalogfile} ]; then
37 | # data=$(cat ${datalogfile} | wc -l)
38 | data=$(tail -n1 ${datalogfile})
39 | fi
40 | if [ ${data} = 3 ]; then
41 | done_data=1
42 | else
43 | done_data=0
44 | fi
45 |
46 | cat << EOF > run/meshes/${mesh_hash}-status.json
47 | {
48 | "status": "running",
49 | "pid": ${gmsh_pid},
50 | "edges": ${edges},
51 | "faces": ${faces},
52 | "volumes": ${volumes},
53 | "data": ${data},
54 | "done_edges": ${done_edges},
55 | "done_faces": ${done_faces},
56 | "done_volumes": ${done_volumes},
57 | "done_data": ${done_data},
58 | "log": "$(tail -n5 run/meshes/${mesh_hash}.1 | cut -d: -f 2- | awk '{printf "%s\\n", $0}')"
59 | }
60 | EOF
61 | # sync run/meshes/${mesh_hash}.json
62 |
63 | else
64 | exit 0
65 | fi
66 |
--------------------------------------------------------------------------------
/cadprocessors/gmsh/cad2stl.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append("../../../../bin")
4 | import gmsh
5 | import os
6 | import math
7 | import random
8 | import colorsys
9 | import json
10 |
11 | def main():
12 | gmsh.initialize()
13 | gmsh.option.setNumber("General.Terminal", 0)
14 |
15 | gmsh.open("original.step")
16 |
17 | solids = len(gmsh.model.getEntities(3))
18 | faces = len(gmsh.model.getEntities(2))
19 | edges = len(gmsh.model.getEntities(1))
20 | vertices = len(gmsh.model.getEntities(0))
21 |
22 | for i in range(vertices):
23 | gmsh.model.addPhysicalGroup(0, [1+i], 1+i)
24 | gmsh.model.setPhysicalName(0, 1+i, "vertex%d" % (1+i))
25 | for i in range(edges):
26 | gmsh.model.addPhysicalGroup(1, [1+i], 1+i)
27 | gmsh.model.setPhysicalName(1, 1+i, "edge%d" % (1+i))
28 | for i in range(faces):
29 | gmsh.model.addPhysicalGroup(2, [1+i], 1+i)
30 | gmsh.model.setPhysicalName(2, 1+i, "face%d" % (1+i))
31 | for i in range(solids):
32 | gmsh.model.addPhysicalGroup(3, [1+i], 1+i)
33 | gmsh.model.setPhysicalName(3, 1+i, "solid%d" % (1+i))
34 |
35 | gmsh.write("cad.xao")
36 | gmsh.finalize()
37 |
38 | # xao to x3d + json
39 | gmsh.initialize()
40 | gmsh.option.setNumber("General.Terminal", 0)
41 | gmsh.open("cad.xao")
42 |
43 | # bounding box global
44 | [xmin, ymin, zmin, xmax, ymax, zmax] = gmsh.model.getBoundingBox(-1, -1)
45 |
46 | # max_length
47 | max_length = math.sqrt(math.pow(xmax-xmin,2) + math.pow(ymax-ymin,2) + math.pow(zmax-zmin,2))
48 | max_delta = max(xmax-xmin, ymax-ymin, zmax-zmin)
49 |
50 | # grabamos el stl
51 | gmsh.option.setNumber("Geometry.OCCBoundsUseStl", 1);
52 | gmsh.option.setNumber("Mesh.StlOneSolidPerSurface", 2)
53 |
54 | # TODO: elegir por commandline
55 | gmsh.option.setNumber("Mesh.StlLinearDeflection", 3e-3*max_delta);
56 | gmsh.option.setNumber("Mesh.StlLinearDeflectionRelative", 1e-2);
57 | gmsh.option.setNumber("Mesh.StlAngularDeflection", 1);
58 |
59 | gmsh.write("cad.stl")
60 | gmsh.write("cad.ply")
61 | gmsh.finalize()
62 |
63 | if __name__ == "__main__":
64 | main()
65 |
66 |
--------------------------------------------------------------------------------
/solvers/ccx/common.php:
--------------------------------------------------------------------------------
1 |
56 |
--------------------------------------------------------------------------------
/solvers/ccx/solving_status.php:
--------------------------------------------------------------------------------
1 |
51 |
--------------------------------------------------------------------------------
/solvers/feenox/solving_status.php:
--------------------------------------------------------------------------------
1 |
51 |
--------------------------------------------------------------------------------
/html/ajax2yaml.php:
--------------------------------------------------------------------------------
1 | SunCAE found a fatal error: ";
11 | echo $error;
12 | echo "
";
13 | exit();
14 | }
15 |
16 |
17 | function return_back_html($response) {
18 | header("Content-Type: text/html");
19 | echo $response;
20 | exit();
21 | }
22 |
23 | function return_error_html($error) {
24 | header("Content-Type: text/html");
25 | echo $response;
26 | exit();
27 | }
28 |
29 | function return_back_json($response) {
30 | header("Content-Type: application/json");
31 | echo json_encode($response);
32 | exit();
33 | }
34 |
35 | function return_error_json($error) {
36 | $response["error"] = $error;
37 | suncae_log($error);
38 | return_back_json($response);
39 | exit();
40 | }
41 |
42 | // based on original work from the PHP Laravel framework
43 | if (!function_exists('str_contains')) {
44 | function str_contains($haystack, $needle) {
45 | return $needle !== '' && mb_strpos($haystack, $needle) !== false;
46 | }
47 | }
48 |
49 | function suncae_log($message) {
50 | global $permissions;
51 | global $username;
52 | $log_dir = __DIR__ . "/../data/logs/";
53 | if (file_exists($log_dir) == false) {
54 | if (mkdir($log_dir, $permissions, true) == false) {
55 | echo "error: cannot create log directory";
56 | exit();
57 | }
58 | }
59 | $log = fopen($log_dir . date("Y-m-d").".log", "a");
60 | if ($log === false) {
61 | echo "Cannot open log file, please check permissions.";
62 | exit(1);
63 | }
64 | fprintf($log, "%s %s\t%s: %s\n", date("c"), $_SERVER['REMOTE_ADDR'], $username, $message);
65 | fclose($log);
66 | }
67 |
--------------------------------------------------------------------------------
/solvers/feenox/common.php:
--------------------------------------------------------------------------------
1 |
64 |
--------------------------------------------------------------------------------
/cadprocessors/gmsh/process.php:
--------------------------------------------------------------------------------
1 | &1", __DIR__), $output, $error_level);
22 |
23 | // TODO: keep output
24 | if ($error_level != 0) {
25 | $response["status"] = "error";
26 | $response["error"] = "Error {$error_level} when importing CAD: ";
27 | for ($i = 0; $i < count($output); $i++) {
28 | $response["error"] .= $output[$i];
29 | }
30 | suncae_log("CAD {$cad_hash} process {$response["status"]} {$response["error"]}");
31 | return_back_json($response);
32 | }
33 | }
34 |
35 | // ------------------------------------------------------------
36 | if (file_exists("cad.json")) {
37 | $cad = json_decode(file_get_contents("cad.json"), true);
38 | $response["position"] = $cad["position"];
39 | $response["orientation"] = $cad["orientation"];
40 | $response["centerOfRotation"] = $cad["centerOfRotation"];
41 | $response["fieldOfView"] = $cad["fieldOfView"];
42 |
43 | } else {
44 | $response["status"] = "error";
45 | $response["error"] = "Cannot create CAD json.";
46 | suncae_log("CAd {$cad_hash} process {$response["status"]} {$response["error"]}");
47 | return_back_json($response);
48 | }
49 |
50 |
51 | // ------------------------------------------------------------
52 | // leave running the mesher in the background
53 | exec("../../../../cadprocessors/gmsh/initial_mesh.sh > cadmesh.log 2>&1 &");
54 |
55 | suncae_log("CAD {$cad_hash} process {$response["status"]} {$response["error"]}");
56 |
57 |
58 | return_back_json($response);
59 |
60 |
--------------------------------------------------------------------------------
/solvers/common.php:
--------------------------------------------------------------------------------
1 | run/${problem_hash}.json
25 | {
26 | "status": "running",
27 | "pid": $$
28 | }
29 | EOF
30 |
31 | cad=$(grep ^cad case.yaml | cut -f2 -d: | tr -d " ")
32 | max_length=$(jq .max_length ../../cads/${cad}/cad.json)
33 |
34 | cp case.fee run/${problem_hash}.fee || exit 2
35 | cd run || exit 3
36 |
37 | # check the syntax
38 | ../../../../../bin/feenox -c ${problem_hash}.fee 1> ${problem_hash}.1 2> ${problem_hash}.2
39 | if [ $? -eq 0 ]; then
40 | # all good!
41 | # see if we have the second-order mesh
42 | if [ ! -e meshes/${mesh_hash}-2.msh ]; then
43 | ../../../../../bin/gmsh -3 meshes/${mesh_hash}.msh -order 2 -o meshes/${mesh_hash}-2.msh > meshes/${mesh_hash}-2.1
44 | fi
45 |
46 | # convert from .fee to .inp
47 | ../../../../../bin/fee2ccx ${problem_hash}.fee > ${problem_hash}.inp
48 |
49 | # run
50 | ../../../../../bin/ccx -i ${problem_hash} 1> ${problem_hash}.1 2> ${problem_hash}.2
51 | ccx_error=$?
52 |
53 | if [ $ccx_error -eq 0 ]; then
54 | if [ "x${problem_type}" = "xmechanical" ]; then
55 | ../../../../../bin/feenox ../../../../../solvers/ccx/frd2vtk.fee ${problem_hash}
56 | ../../../../../bin/feenox ../../../../../solvers/feenox/second2first.fee ${problem_hash} ${mesh_hash}
57 | ../../../../../bin/feenox ../../../../../solvers/feenox/displacements.fee ${problem_hash} ${max_length} | tr -s ' \t\n' ' ' > ${problem_hash}-displacements.dat
58 | ../../../../../bin/feenox ../../../../../solvers/feenox/field1.fee ${problem_hash} sigma | tr -s ' \t\n' ' ' > ${problem_hash}-sigma.dat
59 | fi
60 | status="success"
61 | else
62 | status="error"
63 | fi
64 | else
65 | status="syntax_error"
66 | fi
67 |
68 | cat << EOF > ${problem_hash}.json
69 | {
70 | "status": "${status}"
71 | }
72 | EOF
73 |
74 | # sync run/meshes/${mesh_hash}.json
75 | rm -f ${dir}/${problem_hash}.pid
76 |
--------------------------------------------------------------------------------
/solvers/feenox/solve.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 |
3 | if [ ! -e ./case.fee ]; then
4 | echo "error: run from case directory"
5 | exit 1
6 | fi
7 | if [ ! -d ./run ]; then
8 | echo "error: run dir does not exist"
9 | exit 1
10 | fi
11 | if [ -z "${1}" ]; then
12 | echo "error: specify the problem type"
13 | exit 1
14 | fi
15 |
16 |
17 | # https://stackoverflow.com/questions/3679296/only-get-hash-value-using-md5sum-without-filename
18 | # A simple array assignment works... Note that the first element of a Bash array can be addressed by just the name without the [0] index, i.e., $md5 contains only the 32 characters of md5sum.
19 |
20 | problem_type=${1}
21 | problem_hash=($(md5sum case.fee))
22 | mesh_hash=($(md5sum mesh.geo))
23 |
24 | cat << EOF > run/${problem_hash}.json
25 | {
26 | "status": "running",
27 | "pid": $$
28 | }
29 | EOF
30 |
31 | # cad=$(yq .cad case.yaml | tr -d \")
32 | cad=$(grep ^cad case.yaml | cut -f2 -d: | tr -d " ")
33 | max_length=$(jq .max_length ../../cads/${cad}/cad.json)
34 |
35 | cp case.fee run/${problem_hash}.fee || exit 2
36 | cd run || exit 3
37 |
38 | # check the syntax
39 | ../../../../../bin/feenox -c ${problem_hash}.fee 1> ${problem_hash}.1 2> ${problem_hash}.2
40 | if [ $? -eq 0 ]; then
41 | # all good!
42 | # see if we have the second-order mesh
43 | if [ ! -e meshes/${mesh_hash}-2.msh ]; then
44 | ../../../../../bin/gmsh -3 meshes/${mesh_hash}.msh -order 2 -o meshes/${mesh_hash}-2.msh > meshes/${mesh_hash}-2.1
45 | fi
46 |
47 | # run
48 | # TODO: time & memory (maybe we can read it from the log)
49 | ../../../../../bin/feenox --progress ${problem_hash}.fee 1> ${problem_hash}.1 2> ${problem_hash}.2
50 | feenox_error=$?
51 |
52 | if [ $feenox_error -eq 0 ]; then
53 | if [ "x${problem_type}" = "xmechanical" ]; then
54 | ../../../../../bin/feenox ../../../../../solvers/feenox/second2first.fee ${problem_hash} ${mesh_hash}
55 | ../../../../../bin/feenox ../../../../../solvers/feenox/displacements.fee ${problem_hash} ${max_length} | tr -s ' \t\n' ' ' > ${problem_hash}-displacements.dat
56 | ../../../../../bin/feenox ../../../../../solvers/feenox/field1.fee ${problem_hash} sigma | tr -s ' \t\n' ' ' > ${problem_hash}-sigma.dat
57 | elif [ "x${problem_type}" = "xheat_conduction" ]; then
58 | ../../../../../bin/feenox ../../../../../solvers/feenox/field.fee ${problem_hash} T | tr -s ' \t\n' ' ' > ${problem_hash}-T.dat
59 | fi
60 | status="success"
61 | else
62 | status="error"
63 | fi
64 | else
65 | status="syntax_error"
66 | fi
67 |
68 | cat << EOF > ${problem_hash}.json
69 | {
70 | "status": "${status}"
71 | }
72 | EOF
73 |
74 | # sync run/meshes/${mesh_hash}.json
75 | rm -f ${dir}/${problem_hash}.pid
76 |
--------------------------------------------------------------------------------
/meshers/gmsh/meshing_status.php:
--------------------------------------------------------------------------------
1 |
62 |
--------------------------------------------------------------------------------
/meshers/gmsh/mesh.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -x
2 |
3 | usage="case"
4 | if [ ! -z "${1}" ]; then
5 | usage="cad"
6 | fi
7 |
8 | if [ "x${usage}" == "xcase" ]; then
9 | dir="run"
10 | mesh="mesh"
11 | if [ ! -d ${dir}/meshes ]; then
12 | echo "error: ${dir}/meshes dir does not exist"
13 | exit 1
14 | fi
15 | else
16 | dir="."
17 | mesh="default"
18 | if [ ! -d ${dir}/meshes ]; then
19 | mkdir -p ${dir}/meshes
20 | fi
21 | fi
22 |
23 | if [ ! -e ./${mesh}.geo ]; then
24 | echo "error: run from case directory"
25 | exit 1
26 | fi
27 |
28 | mesh_hash=($(md5sum ${mesh}.geo))
29 |
30 | cat << EOF > ${dir}/meshes/${mesh_hash}.json
31 | {
32 | "status": "running",
33 | "pid": $$
34 | }
35 | EOF
36 |
37 | # TODO: time & memory (maybe we can read it from the log)
38 | ../../../../bin/gmsh -check ${mesh}.geo 1> ${dir}/meshes/${mesh_hash}.1 2> ${dir}/meshes/${mesh_hash}.2
39 | if [ $? -eq 0 ]; then
40 | ../../../../bin/gmsh -3 ${mesh}.geo -o ${dir}/meshes/${mesh_hash}.msh 1> ${dir}/meshes/${mesh_hash}.1 2> ${dir}/meshes/${mesh_hash}.2
41 |
42 | # the meshing could have worked or not, that's in $?
43 | gmsh_error=$?
44 | if [ "x${gmsh_error}" != "x0" ]; then
45 | echo ${dir}/meshes/${mesh_hash}.2 > tmp
46 | cat ${dir}/meshes/${mesh_hash}.2 | grep -v "No elements" | \
47 | grep -v "\-\-\-\-\-\-\-\-\-\-\-" | \
48 | grep -v "Mesh generation error summary" | \
49 | grep -v " warning" | \
50 | grep -v " errors" | \
51 | grep -v "Check the full log for details" > tmp
52 |
53 | sed -n 's/.*intersection (\([^)]*\)).*/\1/p' tmp | tr ',' ' ' | head -n1 > ${dir}/meshes/${mesh_hash}.intersections
54 | mv tmp ${dir}/meshes/${mesh_hash}.2
55 | fi
56 |
57 | # we can have a partial mesh, though
58 | # TODO: rewrite mesh_data in C++
59 | if [ -e ${dir}/meshes/${mesh_hash}.msh ]; then
60 | ../../../../meshers/gmsh/mesh_data.py ${mesh_hash} ${dir}/meshes > ${dir}/meshes/${mesh_hash}-data.log
61 | fi
62 |
63 | # the metadata depends on whether the mesh worked or not
64 | ../../../../meshers/gmsh/mesh_meta.py ${dir}/meshes/${mesh_hash} ${gmsh_error} > ${dir}/meshes/${mesh_hash}.json
65 | if [ -e ${dir}/meshes/${mesh_hash}.gp ]; then
66 | # TODO: bin
67 | gnuplot ${dir}/meshes/${mesh_hash}.gp
68 | fi
69 | # TODO: should we remove this guy?
70 | # rm -f ${dir}/meshes/${mesh_hash}-status.json
71 |
72 | else
73 | cat << EOF > ${dir}/meshes/${mesh_hash}.json
74 | {
75 | "status": "syntax_error"
76 | }
77 | EOF
78 | fi
79 |
80 | # sync run/meshes/${mesh_hash}.json
81 | rm -f ${dir}/${mesh_hash}.pid
82 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 |
2 | # Roadmap
3 |
4 | * more problems (non-linear mechanics, transient thermal, modal, cfd, etc.)
5 | * more meshers (netgen? tetgen? salome?)
6 | * more solvers (sparselizard? fenics?)
7 | * more runners (ssh, aws, kubernetes, etc.)
8 | * more documentation
9 |
10 | # TODOs
11 |
12 | ## General
13 |
14 | * replace python with c++ to "process" msh and make it smarter
15 | * unit tests
16 | * choose units (SI, etc.)
17 | * choose points for BCs (and eventually refinements)
18 | * name in the BC should reflect the content
19 | * dashboard with case list
20 | * real-time collaboration
21 | * detect changes in CAD
22 | * git history in the UX
23 | * show face id when hovering
24 | * screenshots
25 | * once a minute refresh the axes, faces, edges, etc. (take a snapshot?)
26 | * investigate defeature operation in OCC through Gmsh (would we need a separate UX?)
27 | * re-implement how to show SunCAE version in about (when running `deps.sh`)
28 | * check that everything is fine when running `deps.sh`:
29 | - that executables work
30 | - that permissions are fine
31 | - create a `txt` with versions with `git log -1` + `git status --porcelain`
32 | * ability to take notes in markdown
33 | * help ballons, markdown + pandoc
34 | * limit DOFs: in conf? somewhere in auth? like `limits.php`?
35 | * remove visibility, everything is public
36 |
37 |
38 | ## Gmsh
39 |
40 | * STL input
41 | * combos for algorithms
42 | * checkboxes for bool
43 | * local refinements
44 | * understand failures -> train AI to come up with a proper `.geo`
45 | * other meshers! (tetget? netgen?)
46 | * multi-solid: bonded, glued, contact
47 | * curved tetrahedra
48 | * hexahedra
49 |
50 | ## Problem
51 |
52 | * choose faces with ranges e.g 1-74
53 | * other problems: modal
54 | * other solvers: ccx, sparselizard
55 | * orthotropic elasticity
56 | * thermal expansion (isotropic and orthotropic)
57 | * modal feenox
58 | * mechanical sparselizard
59 | * transient/quasistatic (a slider for time?)
60 |
61 | ## Results
62 |
63 | * fields (the list of available fields should be read from the outpt vtk/msh)
64 | - heat flux? (more general, vector fields?)
65 | * the server should tell the client
66 | - which field it is returning (so the client can choose the pallete)
67 | - if it has a warped field or not
68 | * the range scale has to be below the warp slider
69 | * nan color (yellow)
70 | * compute the .dat in the PHP, not in Bash
71 | * probes: user picks location, server returns all field
72 | * reactions: choose which BCs to compute reaction at in the problem step with a checkboxes
73 | * warning for large deformations/stresses
74 |
75 | ## Outer loops
76 |
77 | * parametric
78 | * optimization
79 |
80 | ## Dashboard
81 |
82 | * list of cases
83 |
84 | ## Backlog
85 |
86 | * zoom over mouse
87 | * disable BCs (comment them out)
88 |
--------------------------------------------------------------------------------
/meshers/gmsh/trymesh.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append("../../../../bin")
4 | import gmsh
5 | import math
6 | import os
7 | import json
8 | import signal
9 |
10 | def mesh(attempt):
11 | result = {}
12 | gmsh.initialize()
13 | gmsh.option.setNumber("General.Terminal", 1)
14 | # gmsh.option.setNumber("General.Verbosity", 1) # (0: silent except for fatal errors, 1: +errors, 2: +warnings, 3: +direct, 4: +information, 5: +status, 99: +debug)
15 |
16 | # now we do not care about optimization, then in the actual mesh step we will optimize
17 | gmsh.option.setNumber("Mesh.Optimize", 0)
18 | gmsh.merge("cad.xao")
19 | if attempt != 0:
20 | gmsh.merge("../../../../meshers/gmsh/default%d.geo" % attempt)
21 |
22 | cad_json_file = open("cad.json")
23 | cad = json.load(cad_json_file)
24 | cad_json_file.close()
25 | length_order = math.pow(10, math.floor(math.log10(cad["max_length"])))
26 | length_precision = 1e-2 * length_order
27 | length_char = 4*cad["volume"]/cad["area"]
28 | lc1 = length_char * (1+0.125*(3-attempt))
29 | lc2 = cad["max_length"] / (4 * (1 + attempt))
30 | lc = math.ceil(min([lc1, lc2]) / length_precision) * length_precision
31 | print("length_order", length_order)
32 | print("length_precision", length_precision)
33 | print("length_char", length_char)
34 | print("lc1", lc1)
35 |
36 | gmsh.option.setNumber("Mesh.MeshSizeMax", lc)
37 | gmsh.option.setNumber("Mesh.MeshSizeMin", 1e-2*lc)
38 |
39 | try:
40 | gmsh.model.mesh.generate(3)
41 |
42 | except:
43 | result["result"] = "failed"
44 | nodes, _, _ = gmsh.model.mesh.getNodes()
45 | result["lc"] = lc
46 | result["nodes"] = len(nodes)
47 | result["error"] = gmsh.logger.getLastError()
48 | result["error_entity"] = list(gmsh.model.mesh.getLastEntityError())
49 | # numpy uses uint64 which json does not like
50 | result["error_node"] = list()
51 | for n in gmsh.model.mesh.getLastNodeError():
52 | result["error_node"].append(int(n))
53 | return result
54 |
55 | result["result"] = "success"
56 | nodes, _, _ = gmsh.model.mesh.getNodes()
57 | result["lc"] = lc
58 | result["nodes"] = len(nodes)
59 | gmsh.finalize()
60 | return result
61 |
62 | #def handler(signum, frame):
63 | #signame = signal.Signals(signum).name
64 | #print(f'Signal handler called with signal {signame} ({signum})')
65 | #sys.exit(1)
66 |
67 | # gmsh installs some signal handlers of its own
68 | # I cannot seem to catch signals
69 | #signal.signal(signal.SIGTERM, handler)
70 | #signal.signal(signal.SIGINT, handler)
71 | #signal.signal(signal.SIGKILL, handler)
72 | #signal.signal(signal.SIGHUP, handler)
73 |
74 |
75 | def main():
76 | if len(sys.argv) == 2:
77 | json_file = "attempt%d.json" % (int(sys.argv[1]))
78 | if os.path.exists(json_file):
79 | os.remove(json_file)
80 | result = mesh(int(sys.argv[1]))
81 | with open(json_file, "w") as fp:
82 | json.dump(result, fp, indent=2)
83 |
84 | if __name__ == "__main__":
85 | main()
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/solvers/problems.php:
--------------------------------------------------------------------------------
1 | $problem) {
25 | $keys[$physics] = $physics_name[$physics];
26 | }
27 |
28 | } else if (isset($problems[$what])) {
29 |
30 | // $keys["none"] = "";
31 | foreach ($problems[$what] as $index => $problem) {
32 | $keys[$problem] = $problem_name[$problem];
33 | }
34 |
35 | } else if (isset($solvers[$what])) {
36 |
37 | foreach ($solvers[$what] as $index => $solver) {
38 | $keys[$solver] = $solvers_names[$solver];
39 | }
40 | } else {
41 | // TODO:
42 | $keys["gmsh"] = "Gmsh";
43 |
44 | /*
45 | } else if ($what == "fluid") {
46 |
47 | $keys = ["none", "fluid-irrotational", "fluid-incompressible", "fluid-compressible"];
48 | $values = ["", "Laminar irrotational flow", "Incompressible flow", "Compressible flow"];
49 |
50 | } else if ($what == "thermal") {
51 |
52 | $keys = ["none", "conduction", "radiation"];
53 | $values = ["", "Heat conduction", "Radiative heat exchange"];
54 |
55 | } else if ($what == "electromagnetism") {
56 |
57 | $keys = ["none", "electrostatics", "motor", "antenna"];
58 | $values = ["", "Electrostatics", "Electrical motors", "Antenna radiation"];
59 |
60 | } else if ($what == "acoustics") {
61 |
62 | $keys = ["none", "acoustics-linear", "ultrasonics"];
63 | $values = ["", "Linear acoustics", "Ultrasonics"];
64 |
65 | } else if ($what == "neutron") {
66 |
67 | $keys = ["none", "neutron-diffusion", "neutron-sn"];
68 | $values = ["", "Multigroup diffusion", "Multigroup SN"];
69 |
70 | } else if ($what == "multiphysics") {
71 |
72 | $keys = ["none", "thermo-mechanical", "conjugate-heat", "fluid-structure"];
73 | $values = ["", "Thermomechanical", "Conjugate heat transfer", "Fluid-structure interaction"];
74 | */
75 | }
76 |
77 | $response["keys"] = array();
78 | $response["values"] = array();
79 |
80 | foreach ($keys as $key => $value) {
81 | array_push($response["keys"], $key);
82 | array_push($response["values"], $value);
83 | }
84 |
85 | return_back_json($response);
86 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/solving.php:
--------------------------------------------------------------------------------
1 |
8 |
9 | Solving progress
10 |
11 |
12 |
13 |
14 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
">Second-order mesh
26 |
" role="progressbar">
27 |
28 |
29 |
30 |
Build
31 |
34 |
35 |
Solve
36 |
39 |
40 |
Compute fluxes
41 |
44 |
45 |
51 |
52 |
53 |
54 | Solving...
55 |
56 |
57 |
58 |
59 |
60 |
61 | Cancel solving
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/meshers/gmsh/ajax2mesh.php:
--------------------------------------------------------------------------------
1 | &1", $output, $result);
60 | if ($result != 0) {
61 | for ($i = 0; $i < count($output); $i++) {
62 | if (strncmp("Error", $output[$i], 5) == 0) {
63 | $output_exploded = explode(":", $output[$i]);
64 | for ($j = 1; $j < count($output_exploded); $j++) {
65 | $response["error"] .= $output_exploded[$j] ;
66 | }
67 | $response["error"] .= " ";
68 | }
69 | }
70 | }
71 |
72 | } else {
73 | return_error_json("cannot open mesh.geo");
74 | }
75 | $mesh_hash = mesh_hash();
76 |
77 | exec("git commit -a -m 'mesh {$field} = {$value}'", $output, $result);
78 | if ($result != 0) {
79 | return_error_json("cannot git commit {$id}: {$output[0]}");
80 | }
81 | suncae_log("mesh {$id} ajax2yaml {$field} = {$value}");
82 |
83 | if ($response["error"] != "") {
84 | suncae_log("mesh {$id} error: {$response["error"]}");
85 | }
86 |
87 | return_back_json($response);
88 |
--------------------------------------------------------------------------------
/html/new/create.php:
--------------------------------------------------------------------------------
1 |
8 |
9 | Expert zone
10 |
11 |
12 |
13 |
14 |
19 |
20 |
21 | TODO: mesh
22 |
23 | TOD: vtk
24 |
25 |
26 |
27 |
28 |
33 |
34 |
35 | TODO: show usage
36 |
37 | TODO: clear
38 |
39 |
40 |
41 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/doc/FAQs.md:
--------------------------------------------------------------------------------
1 | # Frequently asked questions
2 |
3 | > [!TIP]
4 | > If your question is not listed here, do not hesitate [contacting us](https://www.seamplex.com/suncae/#contact).
5 |
6 | ### What is the difference between SunCAE, FeenoX, Seamplex and CAEplex?
7 |
8 | * [SunCAE](https://www.seamplex.com/suncae) is an open-source web-based interface for performing CAE in the cloud.
9 | * [FeenoX](https://www.seamplex.com/feenox) is an open-source finite-element solver which used in SunCAE by default.
10 | * [Seamplex](https://www.seamplex.com) is the company that developed both SunCAE and FeenoX
11 | * [CAEplex](https://www.caeplex.com) is the first web-based interface developed by Seamplex. It is [100% integrated into Onshape](https://www.youtube.com/watch?v=ylXAUAsfb5E).
12 |
13 |
14 | ### Can I try SunCAE without having to install it?
15 |
16 | Yes, check out our [live demo](https://www.caeplex.com/suncae).
17 | Note that
18 |
19 | * There is no need to register, but your IP might get logged.
20 | * The data (including CAD files) might (or not) get lost. Do not put sensitive stuff in the demo.
21 |
22 | ### Which language is SunCAE written in?
23 |
24 | The front end is HTML with plain vanilla Javascript. This means no Angular, no React, no NodeJS, nothing. Not even JQuery. Plain vanilla Javascript (plus the Javascript libraries needed for 3D rendering).
25 |
26 | The back end is written in PHP. Again, plain PHP with at most `php-yaml` to read and write YAML files.
27 | The front end would make asynchronous AJAX calls to the back end, which would run PHP file and respond with a JSON content and so the front end can update the DOM.
28 |
29 | ### What are the licensing terms?
30 |
31 | TL;DR: if you use a modified version of SunCAE in your server, you have to provide a link to the modified sources.
32 |
33 | The content of this SunCAE repository is licensed under the terms of the [GNU Affero General Public License version 3](https://www.gnu.org/licenses/agpl-3.0.en.html), or at your option, any later version (AGPLv3+).
34 |
35 | See the [licenses table](../LICENSES.md) and [licensing details](../README.,d#licensing) for more information.
36 |
37 |
38 | ### How does SunCAE import the CAD geometry?
39 |
40 | So far, only using [OpenCASCADE](https://dev.opencascade.org) through the [Gmsh API](https://gitlab.onelab.info/gmsh/gmsh/-/tree/master/api).
41 |
42 | > [!NOTE]
43 | > If you want other CAD imported to be supported, say so in the [forum](https://github.com/seamplex/suncae/discussions).
44 |
45 | ### What are the supported meshers?
46 |
47 | So far, only [Gmsh](http://gmsh.info/) is supported.
48 |
49 | See the directory [`meshers`](https://github.com/seamplex/suncae/tree/main/meshers) for the current list.
50 |
51 | > [!NOTE]
52 | > If you want other meshers to be supported, say so in the [forum](https://github.com/seamplex/suncae/discussions).
53 |
54 |
55 | ### What are the supported solvers?
56 |
57 | * [FeenoX](http://www.seamplex.com/feenox)
58 | * [CalculiX](https://www.dhondt.de/)
59 |
60 | The "single source of truth" is still the FeenoX input file.
61 | CalculiX is supportted through the [`fee2ccx` converter](https://github.com/seamplex/feenox/tree/main/utils/fee2ccx) from `.fee` to `.inp`.
62 |
63 | See the directory [`solvers`](https://github.com/seamplex/suncae/tree/main/solvers) for the current list.
64 |
65 | > [!NOTE]
66 | > If you want other solvers to be supported, say so in the [forum](https://github.com/seamplex/suncae/discussions).
67 |
68 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/meshing.php:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 | Meshing progress
20 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
1D : : 0 /=$cad["edges"]?> edges
36 |
39 |
40 |
2D : : 0 /=$cad["faces"]?> faces
41 |
44 |
45 |
3D : : 0 /=$cad["solids"]?> volumes
46 |
49 |
50 |
Processing
51 |
54 |
55 |
56 |
57 |
58 | Meshing...
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Cancel meshing
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/deps.sh:
--------------------------------------------------------------------------------
1 | #!/bin/false
2 |
3 | bootstrap_version=5.3.3
4 | bootstrap_icons_version=1.11.3
5 | katex_version=0.16.11
6 | pandoc_version=3.5
7 |
8 | # boostrap (we only need the js, the css comes from bootswatch)
9 | echo -n "uxs/faster-than-quick/bootstrap.js... "
10 | bootstrap_tarball=bootstrap-${bootstrap_version}-dist
11 | if [ $force = 1 ] || [ ! -e uxs/faster-than-quick/js/bootstrap.min.js ] || [ ! -f deps/${bootstrap_tarball}.zip ]; then
12 | cd deps
13 | if [ ! -e ${bootstrap_tarball}.zip ]; then
14 | wget https://github.com/twbs/bootstrap/releases/download/v${bootstrap_version}/${bootstrap_tarball}.zip
15 | fi
16 | if [ ! -d ${bootstrap_tarball} ]; then
17 | unzip ${bootstrap_tarball}.zip
18 | fi
19 | cp ${bootstrap_tarball}/js/bootstrap.min.js ../uxs/faster-than-quick/js
20 | cp ${bootstrap_tarball}/js/bootstrap.bundle.min.js ../uxs/faster-than-quick/js
21 | echo "done"
22 | cd ..
23 | else
24 | echo "already installed"
25 | fi
26 |
27 |
28 | # boostrap icons
29 | echo -n "uxs/faster-than-quick/bootstrap icons... "
30 | bootstrap_icons_tarball=bootstrap-icons-${bootstrap_icons_version}
31 |
32 | if [ $force = 1 ] || [ ! -e uxs/faster-than-quick/css/bootstrap-icons.min.css ] || [ ! -f deps/${bootstrap_icons_tarball}.zip ]; then
33 | cd deps
34 | if [ ! -e ${bootstrap_icons_tarball}.zip ]; then
35 | wget https://github.com/twbs/icons/releases/download/v${bootstrap_icons_version}/${bootstrap_icons_tarball}.zip
36 | fi
37 | if [ ! -d ${bootstrap_icons_tarball} ]; then
38 | unzip ${bootstrap_icons_tarball}.zip
39 | fi
40 | cp ${bootstrap_icons_tarball}/font/bootstrap-icons.min.css ../uxs/faster-than-quick/css
41 | mkdir -p ../uxs/faster-than-quick/css/fonts
42 | cp ${bootstrap_icons_tarball}/font/fonts/bootstrap-icons.woff ../uxs/faster-than-quick/css/fonts
43 | cp ${bootstrap_icons_tarball}/font/fonts/bootstrap-icons.woff2 ../uxs/faster-than-quick/css/fonts
44 | for i in Carlito-Bold Carlito-Italic Carlito-BoldItalic Carlito-Regular; do
45 | wget https://raw.githubusercontent.com/googlefonts/carlito/refs/heads/main/fonts/ttf/${i}.ttf -O ../uxs/faster-than-quick/css/fonts/${i}.ttf
46 | done
47 | echo "done"
48 | cd ..
49 | else
50 | echo "already installed"
51 | fi
52 |
53 |
54 | # katex
55 | echo -n "uxs/faster-than-quick/katex... "
56 | if [ $force = 1 ] || [ ! -e uxs/faster-than-quick/css/katex.min.css ]; then
57 | cd deps
58 | if [ ! -e katex.tar.gz ]; then
59 | wget https://github.com/KaTeX/KaTeX/releases/download/v${katex_version}/katex.tar.gz
60 | fi
61 | if [ ! -d katex ]; then
62 | tar xvzf katex.tar.gz
63 | fi
64 | cp katex/katex.min.css ../uxs/faster-than-quick/css
65 | mkdir -p ../uxs/faster-than-quick/css/fonts
66 | cp katex/fonts/* ../uxs/faster-than-quick/css/fonts
67 | echo "done"
68 | cd ..
69 | else
70 | echo "already installed"
71 | fi
72 |
73 | # pandoc
74 | echo -n "uxs/faster-than-quick/pandoc... "
75 | pandoc_tarball=pandoc-${pandoc_version}-linux-amd64
76 | if [ $force = 1 ] || [ ! -e bin/pandoc ] || [ ! -f deps/${pandoc_tarball}.tar.gz ]; then
77 | cd deps
78 | if [ ! -e ${pandoc_tarball}.tar.gz ]; then
79 | wget https://github.com/jgm/pandoc/releases/download/${pandoc_version}/${pandoc_tarball}.tar.gz
80 | fi
81 | if [ ! -d pandoc-${pandoc_version} ]; then
82 | tar xvzf ${pandoc_tarball}.tar.gz
83 | fi
84 | cp pandoc-${pandoc_version}/bin/pandoc ../bin
85 | echo "done"
86 | cd ..
87 | else
88 | echo "already installed"
89 | fi
90 |
91 | # TODO: gnuplot?
92 |
93 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/change_step.php:
--------------------------------------------------------------------------------
1 | 5) {
23 | return_error_json("Invalid step");
24 | }
25 | $next_step = $_POST["next_step"];
26 | $current_step = (isset($_POST["current_step"])) ? $_POST["current_step"] : 2;
27 |
28 | // if the user is coming from mesh but the mesh options has changed, we have to re-mesh and go back
29 | if ($current_step == 1 && $has_mesh_attempt == false) {
30 | $next_step = 1;
31 | }
32 |
33 | switch ($next_step) {
34 | case 1:
35 | // TODO: per-mesher
36 | chdir($case_dir);
37 | if (file_exists("mesh.geo") == false) {
38 | $geo = fopen("mesh.geo", "w");
39 | fprintf($geo, "Merge \"../../cads/{$case["cad"]}/cad.xao\";\n");
40 | fclose($geo);
41 | }
42 |
43 | $response["mesh"] = ($has_mesh) ? $mesh_hash : "";
44 | if ($has_mesh_attempt == false) {
45 | exec("../../../../bin/gmsh -check mesh.geo 1> run/meshes/{$mesh_hash}-check.1 2> run/meshes/{$mesh_hash}-check.2", $output, $result);
46 | if ($result == 0) {
47 | // https://www.php.net/manual/en/function.exec.php
48 | // https://stackoverflow.com/questions/45953/php-execute-a-background-process
49 | exec("../../../../meshers/gmsh/mesh.sh > run/meshing.log 2>&1 & echo $! > run/meshing.pid");
50 | $mesh_meta["status"] = "running";
51 | suncae_log("{$id} mesh running");
52 | } else {
53 | $mesh_meta["status"] = "syntax_error";
54 | suncae_log("{$id} mesh syntax error");
55 | }
56 | file_put_contents("run/meshes/{$mesh_hash}.json", json_encode($mesh_meta));
57 | }
58 | // if running, go to meshing.php otherwise go to mesh.php, it will know what to show}
59 | // TODO: AND or OR?
60 | $next_step *= (isset($mesh_meta["status"]) && $mesh_meta["status"] != "running") ? (+1) : (-1);
61 |
62 | suncae_log("{$id} change_step {$current_step} -> {$next_step}");
63 |
64 | break;
65 |
66 | case 3:
67 | $response["results"] = ($has_results) ? $problem_hash : "";
68 | $response["has_results_attempt"] = $has_results_attempt;
69 | if ($has_results_attempt == false) {
70 |
71 | include("../solvers/{$solver}/change_step_solve.php");
72 | file_put_contents("run/{$problem_hash}.json", json_encode($results_meta));
73 | suncae_log("{$id} change_step {$current_step} -> {$next_step}");
74 |
75 | }
76 | if (isset($results_meta)) {
77 | $next_step *= ($results_meta["status"] != "running") ? (+1) : (-1);
78 | }
79 | break;
80 | }
81 |
82 | $response["step" ] = $next_step;
83 | $response["url"] = "{$step_url[$next_step]}?id={$id}";
84 |
85 |
86 | return_back_json($response);
87 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/importers/upload.php:
--------------------------------------------------------------------------------
1 |
6 |
7 |
48 |
49 |
50 |
51 | 1 CAD file in STEP format
52 |
53 |
64 |
65 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
85 |
86 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/properties.php:
--------------------------------------------------------------------------------
1 |
30 |
31 |
32 |
Visibility
33 |
34 |
35 | >Public
36 | >Private
37 |
38 |
39 |
40 |
41 |
80 |
81 |
--------------------------------------------------------------------------------
/html/case.php:
--------------------------------------------------------------------------------
1 |
107 |
--------------------------------------------------------------------------------
/doc/design.md:
--------------------------------------------------------------------------------
1 | # SunCAE design description
2 |
3 | SunCAE is split into a front end and a back end.
4 |
5 | * The front end is HTML + Javascript running on a web browser on any operating system (even mobile devices).
6 | * The back end is PHP + Bash + Python + particular binaries running on one or more Unix servers.
7 |
8 | The spirit is that the definition of the CAE problem being solved is stored in a single source of truth as a text file, hopefully the actual solver's input file. The web-based interface should allow both
9 |
10 | 1. To update the solver's input file from the status of web-based widgets (e.g. picking faces in the 3D model to hold boundary conditions, entering material properties in text boxes, choosing sdef/ldef from a combo box, etc.), and
11 | 2. To allow the user to edit the input file and then to update the status of web-based widgets from the contents of the input file.
12 |
13 | For instance, consider the following [FeenoX](https://www.seamplex.com/feenox/) input file:
14 |
15 | ```
16 | PROBLEM mechanical READ_MESH meshes/9061bd607902d31a8aac5f97a1066504-2.msh
17 |
18 | E(x,y,z) = 200e3
19 | nu = 0.3
20 |
21 | BC face1 fixed
22 | BC face3 Fx=10e3
23 | ```
24 |
25 | If the user changes the value of the Young's modulus to `210e3`, the front end issues an AJAX call and the back end updates the file.
26 | Conversely, if the user edits the input file (through a web-based editor the UX has to provide) and changes the value in the file to something else, then the back-end sends instructions to the front end to update the web-based interface.
27 | Moreover, each time the file changes (either because of the user changing a value in the interface or editing the file), a Git commit is issued. Therefore, every single change in the single source of truth is tracked (what? who? when?).
28 |
29 | # The SunCAE interface
30 |
31 | The SunCAE interface consists of four steps divided into two stages:
32 |
33 | 1. New case
34 | 1. CAD import, physics, problem, mesher and solver selection
35 | 2. Case view
36 | 1. Mesh
37 | 2. Problem
38 | 3. Results
39 |
40 | The source code of the index page at [html/index.php](../html/index.php) looks like this
41 |
42 | ```php
43 | include("../conf.php");
44 | include("../auths/{$auth}/auth.php");
45 | include("common.php");
46 | include("case.php");
47 | include("../uxs/{$ux}/index.php");
48 | ```
49 |
50 | * The [`conf.php`](../conf.php) file is included first.
51 | * The authorization scheme `$auth` defined in `conf.php` is included.
52 | This step should ask for authentication/authorization, set/read cookies, etc.
53 | PHP's [session_start()](https://www.php.net/manual/en/function.session-start.php) can be used.
54 | This file should define a non-empty global PHP string `$username`.
55 | The default [`single-user`](../auths/single-user/auth.php) auth scheme just does
56 |
57 | ```php
58 | $username = "root";
59 | ```
60 |
61 | * The file `common.php` defines common functions and methods needed by the back end.
62 | It also defines a PHP variable `$id` coming from either a `GET` or `POST` argument with name `id`:
63 |
64 | ```php
65 | $id = (isset($_POST["id"])) ? $_POST["id"] : ((isset($_GET["id"])) ? $_GET["id"] : "");
66 | ```
67 |
68 | This should be the `id` of an existing case.
69 |
70 | * The file `case.php` reads the metadata for the case `id`. But, if the variable `$id` is empty, it will re-direct the user to the ["New case"](#new-case) page at `new/`:
71 |
72 | ```php
73 | if ($id == "") {
74 | header("Location: new/");
75 | exit();
76 | }
77 | ```
78 |
79 | * If `$id` is not empty, the workflow continues to include the `$ux` scheme defined in `conf.php` which should load case `$id` and show the ["Case view"](#case-view), loading either the mesh, problem or results view depending on the state of the case.
80 |
81 |
82 | ## New case
83 |
84 | The [`html/new/index.php`](../html/new/index.php) source is
85 |
86 | ```php
87 | include("../../conf.php");
88 | include("../../auths/{$auth}/auth.php");
89 | include("../common.php");
90 | include("../../uxs/{$ux}/new.php");
91 | ```
92 |
93 | The first three lines have been already discussed.
94 | The default `$ux` is `faster-than-quick`. As its name suggest, it is a quick hack that works.
95 |
96 | To be completed.
97 |
98 | ## Case view
99 |
100 | To be completed.
101 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/bootswatch/_variables.scss:
--------------------------------------------------------------------------------
1 | // CAEplex - based on Flatly 5.0.0-beta2
2 | // Bootswatch
3 |
4 | $theme: "faster-than-quick" !default;
5 |
6 | //
7 | // Color system
8 | //
9 |
10 | $white: #fff !default;
11 | $gray-100: #f8f9fa !default;
12 | $gray-200: #ecf0f1 !default;
13 | $gray-300: #dee2e6 !default;
14 | $gray-400: #ced4da !default;
15 | $gray-500: #b4bcc2 !default;
16 | $gray-600: #95a5a6 !default;
17 | $gray-700: #7b8a8b !default;
18 | $gray-800: #343a40 !default;
19 | $gray-900: #212529 !default;
20 | $black: #000 !default;
21 |
22 | $blue: #2c3e50 !default;
23 | $indigo: #6610f2 !default;
24 | $purple: #6f42c1 !default;
25 | $pink: #e83e8c !default;
26 | $red: #e74c3c !default;
27 | $orange: #fd7e14 !default;
28 | $yellow: #f39c12 !default;
29 | $green: #18bc9c !default;
30 | $teal: #20c997 !default;
31 | $cyan: #3498db !default;
32 |
33 | $primary: #0f85af !default;
34 | $secondary: #a0a050 !default;
35 | $info: #3b4550 !default;
36 |
37 | $success: $green !default;
38 | $warning: #e58c30 !default;
39 | $danger: $red !default;
40 | $light: $gray-200 !default;
41 | $dark: $gray-700 !default;
42 |
43 | $min-contrast-ratio: 2.15 !default;
44 |
45 | // Links
46 |
47 | $link-color: $primary !default;
48 |
49 | // Fonts
50 |
51 | $font-family-sans-serif: Carlito, Arial, sans-serif !default;
52 |
53 | // $font-size-base: 0.9375rem !default;
54 | $font-size-base: 0.9675rem !default;
55 |
56 |
57 | /*
58 | $h1-font-size: 2.10rem !default;
59 | $h2-font-size: 1.85rem !default;
60 | $h3-font-size: 1.50rem !default;
61 | $h4-font-size: 1.25rem !default;
62 | $h5-font-size: 1.15rem !default;
63 | */
64 |
65 | // stylelint-disable-next-line value-keyword-case
66 | // $font-family-sans-serif: Lato, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
67 | $h1-font-size: 3rem !default;
68 | $h2-font-size: 2.5rem !default;
69 | $h3-font-size: 2rem !default;
70 |
71 | // Tables
72 |
73 | $table-accent-bg: $gray-200 !default;
74 |
75 | // Dropdowns
76 |
77 | $dropdown-link-color: $gray-700 !default;
78 | $dropdown-link-hover-color: $white !default;
79 | $dropdown-link-hover-bg: $primary !default;
80 |
81 | // Navs
82 |
83 | $nav-link-padding-y: .5rem !default;
84 | $nav-link-padding-x: 2rem !default;
85 | $nav-link-disabled-color: $gray-600 !default;
86 | $nav-tabs-border-color: $gray-200 !default;
87 |
88 | // Navbar
89 |
90 | $navbar-padding-y: 1rem !default;
91 | $navbar-dark-color: $white !default;
92 | $navbar-dark-hover-color: $success !default;
93 |
94 | // Pagination
95 |
96 | $pagination-color: $white !default;
97 | $pagination-bg: $success !default;
98 | $pagination-border-width: 0 !default;
99 | $pagination-border-color: transparent !default;
100 | $pagination-hover-color: $white !default;
101 | $pagination-hover-bg: darken($success, 15%) !default;
102 | $pagination-hover-border-color: transparent !default;
103 | $pagination-active-bg: $pagination-hover-bg !default;
104 | $pagination-active-border-color: transparent !default;
105 | $pagination-disabled-color: $gray-200 !default;
106 | $pagination-disabled-bg: lighten($success, 15%) !default;
107 | $pagination-disabled-border-color: transparent !default;
108 |
109 | // List group
110 |
111 | $list-group-hover-bg: $gray-200 !default;
112 | $list-group-disabled-bg: $gray-200 !default;
113 |
114 | // Breadcrumbs
115 |
116 | $breadcrumb-padding-y: .375rem !default;
117 | $breadcrumb-padding-x: .75rem !default;
118 | $breadcrumb-border-radius: .25rem !default;
119 |
120 | // Close
121 |
122 | $close-color: $white !default;
123 | $close-text-shadow: none !default;
124 |
125 | // from bootstrap docs
126 | //$breadcrumb-divider: quote(">");
127 | $breadcrumb-divider: quote("»");
128 | /* $breadcrumb-divider: quote("🞂"); */
129 | /* $brseadcrumb-divider: quote("⇒"); */
130 |
131 | $btn-close-color: $white !default;
132 | $btn-close-opacity: .4 !default;
133 | $btn-close-hover-opacity: 1 !default;
134 |
--------------------------------------------------------------------------------
/meshers/gmsh/mesh_data.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | import sys
3 | sys.path.append("../../../../bin")
4 | import gmsh
5 | import os
6 | import json
7 |
8 | # Add a segment, always in increasing order, to make duplicate detection easy
9 | def addLine2(lines, lines_set, tags, first, second):
10 | a = int(tags[first])
11 | b = int(tags[second])
12 | entry = (min(a-1, b-1), max(a-1, b-1))
13 | if entry not in lines_set:
14 | lines.append([entry[0], entry[1]])
15 | lines_set.add(entry)
16 | return
17 |
18 | def addLine3(lines, lines_set, tags, first, second, third):
19 | a = int(tags[first])
20 | b = int(tags[second])
21 | c = int(tags[third])
22 | # Always store in the lowest, middle, largest order (for consistency)
23 | sorted_entry = tuple(sorted([a-1, b-1, c-1]))
24 | if sorted_entry not in lines_set:
25 | lines.append(list(sorted_entry))
26 | lines_set.add(sorted_entry)
27 | return
28 |
29 | # ------------------------------------------
30 |
31 | if (len(sys.argv) < 3):
32 | print("need mesh hash and dir in the command line")
33 | sys.exit(1)
34 |
35 | mesh_file = "%s/%s.msh" % (sys.argv[2], sys.argv[1])
36 | if not os.path.exists(mesh_file):
37 | print("mesh file does not exist")
38 | sys.exit(1)
39 |
40 | print("1", flush=True)
41 | gmsh.initialize()
42 | gmsh.option.setNumber("General.Terminal", 0)
43 | gmsh.open(mesh_file)
44 |
45 | # TODO: read whether the mesh is curved or not
46 | curved = 0
47 |
48 | mesh = {}
49 |
50 | # nodes ------------
51 | mesh["nodes"] = ""
52 | tags, coord, _ = gmsh.model.mesh.getNodes()
53 | maxtag = max(tags)
54 | for i in range(maxtag):
55 | mesh["nodes"] += "{:6g} {:6g} {:6g} ".format(coord[3*i+0], coord[3*i+1], coord[3*i+2])
56 |
57 | # surface edges ------------
58 | mesh["surfaces_edges_set"] = ""
59 | elements = gmsh.model.mesh.getElements(2)
60 | n_elements = len(elements)
61 | lines = []
62 | lines_set = set()
63 | i = 0
64 | k = 0
65 | for type in elements[0]:
66 | j = 0
67 | if type == 2 or (curved == 0 and type == 9): # 3-node triangle
68 | for element in elements[1][i]:
69 | addLine2(lines, lines_set, elements[2][i], j+0, j+1)
70 | addLine2(lines, lines_set, elements[2][i], j+1, j+2)
71 | addLine2(lines, lines_set, elements[2][i], j+2, j+0)
72 | j += 3 if type == 2 else 6
73 | k += 1
74 | elif type == 9: # 6-node triangle
75 | for element in elements[1][i]:
76 | addLine3(lines, lines_set, elements[2][i], j+0, j+3, j+1)
77 | addLine3(lines, lines_set, elements[2][i], j+1, j+4, j+2)
78 | addLine3(lines, lines_set, elements[2][i], j+2, j+5, j+0)
79 | j += 6
80 | k += 1
81 | i += 1
82 |
83 | print("2", flush=True)
84 |
85 | n_lines = len(lines)
86 | k = 0
87 | for line in lines:
88 | if (len(line) == 2):
89 | mesh["surfaces_edges_set"] += "{:d} {:d} -1 ".format(line[0], line[1])
90 | elif (len(line) == 3):
91 | mesh["surfaces_edges_set"] += "{:d} {:d} {:d} -1 ".format(line[0], line[1], line[2])
92 | k += 1
93 | print("3", flush=True)
94 |
95 | # surface faces, one per each physical group ------------
96 | mesh["surfaces_faces_set"] = {}
97 | physicals = gmsh.model.getPhysicalGroups()
98 | k = 0
99 | for physical in physicals:
100 | dim = physical[0]
101 | physical_tag = physical[1]
102 | if (dim == 2):
103 | mesh["surfaces_faces_set"][physical_tag] = ""
104 | for entity in gmsh.model.getEntitiesForPhysicalGroup(dim, physical_tag):
105 | types, tags, nodetags = gmsh.model.mesh.getElements(dim, entity)
106 | for i in range(len(types)):
107 | for j in range(len(tags[i])):
108 | if types[i] == 2 or (curved == 0 and types[i] == 9):
109 | N = 6 if (types[i] == 9) else 3
110 | # for triangles remove the last int and the -1
111 | mesh["surfaces_faces_set"][physical_tag] += "{:d} {:d} {:d} ".format(int(nodetags[i][j*N+0])-1, int(nodetags[i][j*N+1])-1, int(nodetags[i][j*N+2])-1)
112 | elif types[i] == 9:
113 | N = 6
114 | mesh["surfaces_faces_set"][physical_tag] += "{:d} {:d} {:d} ".format(int(nodetags[i][j*N+0])-1, int(nodetags[i][j*N+3])-1, int(nodetags[i][j*N+5])-1)
115 | mesh["surfaces_faces_set"][physical_tag] += "{:d} {:d} {:d} ".format(int(nodetags[i][j*N+1])-1, int(nodetags[i][j*N+4])-1, int(nodetags[i][j*N+3])-1)
116 | mesh["surfaces_faces_set"][physical_tag] += "{:d} {:d} {:d} ".format(int(nodetags[i][j*N+2])-1, int(nodetags[i][j*N+5])-1, int(nodetags[i][j*N+4])-1)
117 | mesh["surfaces_faces_set"][physical_tag] += "{:d} {:d} {:d} ".format(int(nodetags[i][j*N+3])-1, int(nodetags[i][j*N+4])-1, int(nodetags[i][j*N+5])-1)
118 | k += 1
119 |
120 | print("4", flush=True)
121 | gmsh.finalize()
122 |
123 | with open("%s/%s-data.json" % (sys.argv[2], sys.argv[1]), "w", encoding ='utf8') as json_file:
124 | json.dump(mesh, json_file)
125 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/small_axes.html:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/results.php:
--------------------------------------------------------------------------------
1 |
19 |
20 | The solving process was canceled.
21 |
22 |
23 |
24 |
25 | Re-launch solver
26 |
27 |
30 | =file_get_contents("{$case_dir}/run/{$problem_hash}-check.2")?>
31 |
35 | =file_get_contents("{$case_dir}/run/{$problem_hash}.2")?>
36 |
37 | Re-launch solver
38 |
39 |
42 | Got status error but no stderr.
43 |
47 | Not sure what happened.
48 |
53 |
54 | There are no results nor any attempt at getting them.
55 |
56 |
61 |
73 |
79 | Not yet implemented
80 |
86 | Not yet implemented
87 |
98 |
99 |
100 |
101 |
102 | =$console?>
103 |
104 |
105 |
110 |
111 |
112 |
113 |
114 |
115 | Mesh
116 |
117 |
118 |
119 |
120 | Problem
121 |
122 |
123 |
124 |
125 |
">
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | Real warp
135 |
136 |
137 |
138 |
139 | 0) {
141 | ?>
142 |
143 |
146 |
147 |
150 |
--------------------------------------------------------------------------------
/doc/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
37 |
42 |
43 |
45 |
50 |
57 |
61 |
65 |
70 |
75 |
80 |
81 |
82 |
88 | Sun
99 |
100 |
101 |
--------------------------------------------------------------------------------
/uxs/faster-than-quick/bootswatch/_bootswatch.scss:
--------------------------------------------------------------------------------
1 | // CAEplex - based on Flatly 5.0.0-beta2
2 | // Bootswatch
3 |
4 | code {
5 | color: $secondary;
6 | }
7 |
8 | @font-face {
9 | font-family: Carlito;
10 | src: url(fonts/Carlito-Regular.ttf);
11 | }
12 |
13 | @font-face {
14 | font-family: Carlito;
15 | src: url(fonts/Carlito-Bold.ttf);
16 | font-weight: bold;
17 | }
18 |
19 | @font-face {
20 | font-family: Carlito;
21 | src: url(fonts/Carlito-Italic.ttf);
22 | font-style: italic;
23 | }
24 |
25 | @font-face {
26 | font-family: Carlito;
27 | src: url(fonts/Carlito-BoldItalic.ttf);
28 | font-weight: bold;
29 | font-style: italic;
30 | }
31 |
32 |
33 | // Navbar
34 |
35 | .bg-primary {
36 | .navbar-nav .active > .nav-link {
37 | color: $success !important;
38 | }
39 | }
40 |
41 | .bg-dark {
42 | &.navbar-dark .navbar-nav {
43 | .nav-link:focus,
44 | .nav-link:hover,
45 | .active > .nav-link {
46 | color: $primary !important;
47 | }
48 | }
49 | }
50 |
51 | // Buttons
52 |
53 | .btn {
54 | &-secondary,
55 | &-secondary:hover,
56 | &-warning,
57 | &-warning:hover {
58 | color: $white;
59 | }
60 | }
61 |
62 | // Tables
63 |
64 | .table {
65 | &-primary,
66 | &-secondary,
67 | &-success,
68 | &-info,
69 | &-warning,
70 | &-danger {
71 | color: $white;
72 | }
73 |
74 | &-primary {
75 | &,
76 | > th,
77 | > td {
78 | background-color: $primary;
79 | }
80 | }
81 |
82 | &-secondary {
83 | &,
84 | > th,
85 | > td {
86 | background-color: $secondary;
87 | }
88 | }
89 |
90 | &-light {
91 | &,
92 | > th,
93 | > td {
94 | background-color: $light;
95 | }
96 | }
97 |
98 | &-dark {
99 | &,
100 | > th,
101 | > td {
102 | background-color: $dark;
103 | }
104 | }
105 |
106 | &-success {
107 | &,
108 | > th,
109 | > td {
110 | background-color: $success;
111 | }
112 | }
113 |
114 | &-info {
115 | &,
116 | > th,
117 | > td {
118 | background-color: $info;
119 | }
120 | }
121 |
122 | &-danger {
123 | &,
124 | > th,
125 | > td {
126 | background-color: $danger;
127 | }
128 | }
129 |
130 | &-warning {
131 | &,
132 | > th,
133 | > td {
134 | background-color: $warning;
135 | }
136 | }
137 |
138 | &-active {
139 | &,
140 | > th,
141 | > td {
142 | background-color: $table-active-bg;
143 | }
144 | }
145 |
146 | &-hover {
147 | .table-primary:hover {
148 | &,
149 | > th,
150 | > td {
151 | background-color: darken($primary, 5%);
152 | }
153 | }
154 |
155 | .table-secondary:hover {
156 | &,
157 | > th,
158 | > td {
159 | background-color: darken($secondary, 5%);
160 | }
161 | }
162 |
163 | .table-light:hover {
164 | &,
165 | > th,
166 | > td {
167 | background-color: darken($light, 5%);
168 | }
169 | }
170 |
171 | .table-dark:hover {
172 | &,
173 | > th,
174 | > td {
175 | background-color: darken($dark, 5%);
176 | }
177 | }
178 |
179 | .table-success:hover {
180 | &,
181 | > th,
182 | > td {
183 | background-color: darken($success, 5%);
184 | }
185 | }
186 |
187 | .table-info:hover {
188 | &,
189 | > th,
190 | > td {
191 | background-color: darken($info, 5%);
192 | }
193 | }
194 |
195 | .table-danger:hover {
196 | &,
197 | > th,
198 | > td {
199 | background-color: darken($danger, 5%);
200 | }
201 | }
202 |
203 | .table-warning:hover {
204 | &,
205 | > th,
206 | > td {
207 | background-color: darken($warning, 5%);
208 | }
209 | }
210 |
211 | .table-active:hover {
212 | &, > th, > td {
213 | background-color: $table-active-bg;
214 | }
215 | }
216 |
217 | }
218 | }
219 |
220 | // Navs
221 |
222 | .nav-tabs {
223 | .nav-link.active,
224 | .nav-link.active:focus,
225 | .nav-link.active:hover,
226 | .nav-item.open .nav-link,
227 | .nav-item.open .nav-link:focus,
228 | .nav-item.open .nav-link:hover {
229 | color: $primary;
230 | }
231 | }
232 |
233 | .pagination {
234 | a:hover {
235 | text-decoration: none;
236 | }
237 | }
238 |
239 | // Indicators
240 |
241 | .badge {
242 | &-secondary,
243 | &-warning {
244 | color: $white;
245 | }
246 | }
247 |
248 | .alert {
249 | border: none;
250 | color: $white;
251 |
252 | a,
253 | .alert-link {
254 | color: $white;
255 | font-weight: bold;
256 | }
257 |
258 | @each $color, $value in $theme-colors {
259 | &-#{$color} {
260 | @if $enable-gradients {
261 | background: $value linear-gradient(180deg, mix($body-bg, $value, 15%), $value) repeat-x;
262 | } @else {
263 | background-color: $value;
264 | }
265 | }
266 | }
267 |
268 | &-light {
269 | &,
270 | a,
271 | .alert-link {
272 | color: $body-color;
273 | }
274 | }
275 | }
276 |
277 | // Containers
278 |
279 | .modal,
280 | .toast {
281 | .btn-close {
282 | background-image: url("data:image/svg+xml,
");
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/doc/INSTALL.md:
--------------------------------------------------------------------------------
1 | # Installing and setting up SunCAE
2 |
3 | > [!TIP]
4 | > If you just want to use SunCAE without installing it, you can do so with the [live demo](https://www.caeplex.com/suncae).
5 |
6 | > [!NOTE]
7 | > Mind the license of SunCAE itself and the license of all the related packages that SunCAE uses to make sure you are not infringing any license.
8 |
9 | This document explains how to set up [SunCAE](https://www.seamplex.com/suncae) so as to serve one or more clients.
10 | A basic installation can be done relatively simple, even without understanding the meaning of the commands.
11 | Keep in mind that a full-fledged installation being able to serve different users might need deep understanding of networking administration and operating systems details.
12 |
13 | ## Architectures
14 |
15 | The code is aimed at being run on Unix systems. Specifically, Debian GNU/Linux.
16 | There might be ways of making SunCAE run on other architectures.
17 | If you happen to know how, please help us by explaining how.
18 |
19 | ## Cloning the repository
20 |
21 | The first step would be to clone the SunCAE repository:
22 |
23 | ```
24 | git clone https://github.com/seamplex/suncae
25 | cd suncae
26 | ```
27 |
28 |
29 | ## Dependencies
30 |
31 | The repository only hosts particular code and files which are not already available somewhere else.
32 | The latter include
33 |
34 | * meshers and solvers executables (e.g. `gmsh`, `feenox`, `ccx`, etc.)
35 | * Javascript libraries (e.g. `x3dom.js`)
36 | * CSS and fonts (e.g. `bootstrap.css`)
37 |
38 |
39 | ### Common
40 |
41 | SunCAE needs some functionality which is provided by packages which are commonly available in the most common GNU/Linux distribution repositories. Ranging from the web server itself, script interpreters (e.g. PHP and Bash) and other standard Unix utilities, this line (or a similar one for a non-Debian distribution) should be enough:
42 |
43 | ```
44 | sudo apt-get install git unzip patchelf wget php-cli php-yaml gnuplot
45 | ```
46 |
47 | ### Particular
48 |
49 | The (free and open source) meshers, solvers and required libraries and fonts can be downloaded by executing the `deps.sh` script in SunCAE's root directory:
50 |
51 | ```
52 | ./deps.sh
53 | ```
54 |
55 | > [!IMPORTANT]
56 | > Run the script from SunCAE's root directory, i.e.
57 | >
58 | > ```
59 | > ./deps.sh
60 | > ```
61 | >
62 | > and **not** from the parent (or any other directory) like
63 | >
64 | > ```
65 | > suncae/dep.sh
66 | > ```
67 |
68 | > [!TIP]
69 | > The script will try to download and copy the dependencies inside SunCAE's directories (ignored by Git) only if they are not already copied. To force the download and copy (say if the version changed), you can either delete the dependencies or pass `--force` to `deps.sh`
70 | >
71 | > ```
72 | > ./deps.sh --force
73 | > ```
74 |
75 |
76 | ## The web server
77 |
78 | SunCAE can be hosted with any web server capable of executing PHP scripts.
79 | The main entry point is under directory `html`.
80 |
81 | All the user information is stored as files under the directory `data`.
82 | That is to say, there is not **database** (either SQL or Mongo-like).
83 | Just plain (Git-tracked) files.
84 |
85 | > [!TIP]
86 | > Backups are as simple as `cp`ing (or `rsync`ing, `tar`ring, etc.) the directory `data` somewhere else.
87 |
88 |
89 |
90 | ### PHP's internal web server
91 |
92 | The `php-cli` package includes a simple web server which is enough to host SunCAE for single-user mode (and it is even handy for debugging purposes).
93 | Just run `php` with the `-S` option. Choose an available port and pass the `html` directory in the `-t` option (or go into the `html` directory and run `php -S` from there without `-t`):
94 |
95 | ```terminal
96 | php -S localhost:8000 -t html
97 | ```
98 |
99 | > [!IMPORTANT]
100 | > The first time that SunCAE needs to use the `data` directory, it will be created and owned by the user running the server, which in this case will be the user that ran `php`.
101 | > Mind ownerships and permissions if you then change from the internal web server to a professional one such as Apache.
102 |
103 | ### Apache
104 |
105 | Configure Apache to serve the `html` directory in SunCAE's repository.
106 | By default, Apache's root directory is `/var/www/html`.
107 |
108 | A quick hack is to make sure that SunCAE’s [`html`](html) directory is available to be served. For instance, in the default installation you could do
109 |
110 | ```terminal
111 | ln -s html /var/www/html/suncae
112 | ```
113 |
114 | and then SunCAE would be available at
.
115 |
116 | > [!WARNING]
117 | > Mind Apache's policies about symbolic links. They are not straightforward, so symlinking SunCAE's `html` directory into Apache's `html` directory might not work out of the box.
118 |
119 |
120 | * If you do not have experience with Apache, you might want to delete the default `/var/www` tree and clone SunCAE there.
121 | * If you have experience with Apache, there is little more to add.
122 |
123 |
124 | > [!IMPORTANT]
125 | > The first time that SunCAE needs to use the `data` directory, it will be created and owned by the user running the server, which in this case by default is `www-data`.
126 | > Mind ownerships and permissions when accessing `data`.
127 |
128 | ### Other servers
129 |
130 | We do not know.
131 |
132 |
133 | ## Stack configuration
134 |
135 | The file [`conf.php`](../conf.php) in SunCAE's root directory controls the choices of the implementations of the different components for the current instance of SunCAE being served:
136 |
137 | ```php
138 | $auth = "single-user";
139 | $ux = "faster-than-quick";
140 | $cadimporter = "upload";
141 | $cadprocessor = "gmsh";
142 | $runner = "local";
143 | $max_nodes = 100e3;
144 | ```
145 |
146 | This means that
147 |
148 | 1. the same server can change the implementations by changing the content of `conf.php` dynamically
149 | 2. different servers (or the same server with different entry points) can serve different implementations by serving different `html` directories whose parent's `conf.php` is different.
150 | 3. any other combination is also possible, e.g. an interactive HTML-based panel that modifies `conf.php` on demand or that clones a new instance of SunCAE in an arbitrary location (and configures Apache to serve it).
151 |
152 | Read the [Design Manual](design.md) for details about what each of the definitions mean.
153 |
154 |
--------------------------------------------------------------------------------