├── .dockerignore ├── .gitignore ├── COPYING ├── Dockerfile ├── LICENSE ├── README.md ├── TODO.md ├── auths ├── htpasswd │ ├── .htaccess │ ├── .htpasswd │ ├── README.md │ └── auth.php └── single-user │ └── auth.php ├── bin └── .gitignore ├── cadimporters └── upload │ ├── gmshcheck.py │ └── import_cad.php ├── cadprocessors └── gmsh │ ├── cad2stl.py │ ├── cadcheck.py │ ├── cadimport.py │ ├── initial_mesh.sh │ └── process.php ├── conf.php ├── data └── .gitignore ├── deps.sh ├── doc ├── FAQs.md ├── INSTALL.md ├── README.md ├── licenses.md ├── licensing.md ├── logo.svg ├── tutorials.md ├── virtual.md └── workflow.md ├── html ├── .htaccess ├── ajax2mesh.php ├── ajax2problem.php ├── ajax2yaml.php ├── assets │ └── named_cube.x3d ├── cad.php ├── case.php ├── change_step.php ├── common.php ├── css │ └── faster-than-quick ├── expert.php ├── img │ └── logo.svg ├── index.php ├── js │ ├── faster-than-quick │ └── index.html ├── mesh.php ├── mesh_data.php ├── mesh_graph.php ├── mesh_inp_save.php ├── mesh_inp_show.php ├── mesh_log.php ├── mesh_msh.php ├── meshing.php ├── meshing_cancel.php ├── meshing_relaunch.php ├── meshing_status.php ├── new │ ├── create.php │ ├── img │ │ └── logo.svg │ ├── import_cad.php │ ├── index.php │ ├── preview.php │ ├── problems.php │ ├── process.php │ └── sample.step ├── problem.php ├── problem_fee.php ├── problem_fee_save.php ├── properties.php ├── results.php ├── results_data.php ├── results_vtk.php ├── solving.php ├── solving_relaunch.php └── solving_status.php ├── meshers ├── README.md └── gmsh │ ├── .gitignore │ ├── LICENSE │ ├── ajax2mesh.php │ ├── cadmesh.py │ ├── default0.geo │ ├── default1.geo │ ├── default2.geo │ ├── default3.geo │ ├── default4.geo │ ├── default5.geo │ ├── deps.sh │ ├── mesh.sh │ ├── mesh_data.php │ ├── mesh_data.py │ ├── mesh_graph.php │ ├── mesh_inp_save.php │ ├── mesh_inp_show.php │ ├── mesh_log.php │ ├── mesh_meta.py │ ├── mesh_status.sh │ ├── meshing_cancel.php │ ├── meshing_relaunch.php │ ├── meshing_status.php │ ├── process_step.php │ ├── quality.gp │ ├── quality.py │ └── trymesh.py ├── renderers └── x3dom │ ├── .gitignore │ ├── LICENSE │ └── deps.sh ├── solvers ├── README.md ├── ccx │ ├── LICENSE │ ├── ajax2problem.php │ ├── change_step_solve.php │ ├── common.php │ ├── deps.sh │ ├── frd2vtk.fee │ ├── input_initial_mechanical.php │ ├── problem_fee.php │ ├── problem_fee_save.php │ ├── problems.php │ ├── results_data.php │ ├── solve.sh │ ├── solve_status.sh │ └── solving_status.php ├── common.php ├── feenox │ ├── LICENSE │ ├── ajax2problem.php │ ├── change_step_solve.php │ ├── common.php │ ├── deps.sh │ ├── displacements.fee │ ├── feenox.xml │ ├── field.fee │ ├── field1.fee │ ├── input_initial_heat_conduction.php │ ├── input_initial_mechanical.php │ ├── problem_fee.php │ ├── problem_fee_save.php │ ├── problems.php │ ├── results_data.php │ ├── results_data │ │ ├── heat_conduction.php │ │ └── mechanical.php │ ├── second2first.fee │ ├── solve.sh │ ├── solve_status.sh │ ├── solving_relaunch.php │ └── solving_status.php ├── problems.php └── sparselizard │ ├── LICENSE │ └── problems.php └── uxs ├── .gitignore └── faster-than-quick ├── about.php ├── bootswatch ├── .gitignore ├── _bootswatch.scss ├── _variables.scss └── make.sh ├── cad.php ├── change_step.php ├── css ├── .gitignore ├── bootstrap.min.css ├── ftq.css ├── highlight.css └── index.html ├── deps.sh ├── expert.php ├── importers └── upload.php ├── index.php ├── js ├── .gitignore ├── ftq.js ├── heat_conduction.js ├── index.html └── mechanical.js ├── labels.php ├── labels ├── .gitignore ├── README.md ├── labels.awk ├── labels.sh └── labels.txt ├── mesh.php ├── mesh └── gmsh.php ├── meshing.php ├── named_cube.x3d ├── new.php ├── preview.php ├── problem.php ├── problem ├── heat_conduction.php └── mechanical.php ├── properties.php ├── results.php ├── results ├── heat_conduction.php └── mechanical.php ├── share.php ├── small_axes.html ├── solving.php └── ux.php /.dockerignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | deps 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2.20-apache AS final 2 | RUN apt-get update && apt-get install -y \ 3 | python3 \ 4 | && rm -rf /var/lib/apt/lists/* \ 5 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" 6 | COPY . /var/www 7 | RUN mkdir -p /var/www/data && chown www-data:www-data /var/www 8 | USER www-data 9 | 10 | # docker build -t suncae:2024-9-14 . 11 | # docker run -p 9000:80 suncae:2024-9-14 12 | # docker ps 13 | # docker exec -it bash 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Anything that does not have either notice a LICENSE in the directory is AGPLv3+. 2 | See the file COPYING for details. 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SunCAE: Simulations in your browser 2 | 3 | ![](doc/logo.svg) 4 | 5 | > A free and open source web-based platform for performing CAE in the cloud. 6 | 7 | ## What 8 | 9 | SunCAE is a web-based front end and a cloud-based back end to perform finite-element calculations directly on the browser. 10 | 11 | * The front end is an HTML client with plain vanilla javascript code (i.e. no Angular, no React, not even jQuery) that provide interactivity by sending JSON-based AJAX request to the server backe end. 12 | * The back end is written in PHP and responds to the client requests by creating the necesary files, executing the necesary binaries and returning back 3D data to the client. 13 | 14 | Both fron end and back ends are free software, released under the terms of the [AGPLv3](https://www.gnu.org/licenses/agpl-3.0.en.html). See [licensing](#licensing) below for more information. 15 | 16 | 17 | ## Why 18 | 19 | 1. **No need to install.** You can use it [online](https://www.caeplex.com/suncae) because it is web-based. 20 | 2. **Single source of truth.** All your data is in a single location because it is cloud-first. 21 | 3. **Increased traceability.** All changes are tracked using Git. 22 | 4. **Collaboration.** Many users can access the same case. And Git keeps track of who changed what when. 23 | 5. **Mobile-friendly.** Access your simulation project from your phone or tablet. 24 | 6. **Free and open source.** As in "free speech." You fully get the four freedoms. 25 | 7. **Extensible.** Add more meshers, solvers, UXs, etc. Join the community! 26 | 27 | 28 | ## How 29 | 30 | You can use SunCAE either by... 31 | 32 | 1. using someone else’s servers and configurations 33 | 34 | * open [this link to use SunCAE in our live demo](https://www.caeplex.com/suncae) 35 | * check out these Youtube videos to learn how to use it 36 | - [Tutorial #1: Overview](https://youtu.be/MYl7-tcCAfE) (4 min) 37 | - [Tutorial #2: NAFEMS LE10](https://youtu.be/ANQX0EZI_q8) (4 min) 38 | - [Tutorial #3: Heat conduction](https://youtu.be/WeEeZ5BVm8I) (3.5 min) 39 | 40 | 41 | 2. hosting your own server (it can be your laptop!) so you (or other people) can use it: 42 | 43 | 1. install some common dependencies 44 | 2. clone the SunCAE repository 45 | 3. run a script to fetch the open source CAE-related tools (renderers, solvers, meshers, etc.): 46 | 47 | ```terminal 48 | sudo apt-get install git 49 | git clone https://github.com/seamplex/suncae 50 | cd suncae 51 | sudo apt-get install unzip patchelf wget php-cli php-yaml gnuplot 52 | ./deps.sh 53 | php -S localhost:8000 -t html 54 | ``` 55 | 4. open with a web browser 56 | 57 | > [!NOTE] 58 | > SunCAE is aimed at the cloud. The cloud likes Unix (and Unix likes the cloud). 59 | > So these instructions apply to Unix-like servers, in particular GNU/Linux. 60 | > There might be ways to run SunCAE on Windows, but we need time to figure out what they are. 61 | > 62 | > Moreover, most CAE solvers do not perform in Windows. 63 | > There is a simple explanation: (good) solvers are written by hackers. 64 | > And hackers---as [Paul Graham already explained more than twenty years ago](https://paulgraham.com/gh.html)---do not like Windows (and Windows do not like hackers either). 65 | 66 | 67 | For more detailed instructions including setting up production web servers and using virtualization tools (e.g. docker and/or virtual machines) read the [installation guide](doc/INSTALL.md). 68 | 69 | 70 | ### Configuration 71 | 72 | With SunCAE---as with sundae ice creams---you get to choose the toppings: 73 | 74 | 1. [Authenticators](auths) 75 | * [single-user](auths/single-user) 76 | * [htpasswd](auths/htpasswd) 77 | * ... 78 | 2. [UXs](uxs) 79 | * [faster-than-quick](uxs/faster-than-quick) 80 | * ... 81 | 3. [CAD importers](cadimporters) 82 | * [upload](cadimporters/upload) 83 | * ... 84 | 4. [CAD processors](cadprocessors) 85 | * [gmsh](cadprocessors/gmsh) 86 | * ... 87 | 5. Runners (to be done, e.g. local, ssh, aws, ...) 88 | 6. Post processors (to be done, e.g. paraview, glvis, ...) 89 | 90 | Moreover, for each case users can choose the combination of 91 | 92 | * [Meshers](meshers) (e.g. [gmsh](meshers/gmsh, ...) 93 | * [Solvers](solvers) (e.g. [feenox](solvers/feenox), [calculix](solvers/calculix), ...) 94 | 95 | 96 | ## Licensing 97 | 98 | 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+). 99 | 100 | This means that you get the four essential freedoms, so you can 101 | 102 | 0. Run SunCAE as you seem fit (i.e. for any purpose). 103 | 1. Investigate the source code to see how SunCAE works and to change it (or to hire someone to change it four you) as you seem fit (i.e. to suit your needs) 104 | 2. Redistribute copies of the source code as you seem fit (i.e. to help your neighbor) 105 | 3. Publish your changes (or the ones that the people you hired made) to the public (i.e. to benefit the community). 106 | 107 | > [!IMPORTANT] 108 | > With great power comes great responsibility. 109 | 110 | If you use a _modified_ version of SunCAE in your web server, [section 13 of the AGPL license](https://www.gnu.org/licenses/agpl-3.0.en.html#section13) requires you to give a link where your users can get these four freedoms as well. 111 | That is to say, if you use a verbatim copy of SunCAE in your server, there is nothing for you to do (because the link is already provided). 112 | But if you exercise freedoms 1 & 3 above and _modify_ SunCAE to suit your needs---let's say you don't like the button "Add Boundary Condition" and you change it to "Add restrains and loads"---you do need to provide a link for people to download the modified source code. 113 | 114 | > [!TIP] 115 | > If this licensing scheme does not suit you, contact us to see how we can make it work. 116 | 117 | * If you have a solver released under a license which is compatible with the AGPL and you would like to add it to SunCAE, feel free to fork the repository (and create a pull request when you are done). 118 | * If you have a solver released under a license which is not compatible with the AGPL and you would like to add it to SunCAE, contact us. 119 | 120 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | * more problems (non-linear mechanics, transient thermal, modal, cfd, etc.) 4 | * more meshers (netgen? tetgen? salome?) 5 | * more solvers (sparselizard? ccx? fenics?) 6 | * more runners (ssh, aws, kubernetes, etc.) 7 | * more documentation 8 | 9 | # TODOs 10 | 11 | ## General 12 | 13 | * unit tests 14 | * choose units (SI, etc.) 15 | * choose points for BCs (and eventually refinements) 16 | * name in the BC should reflect the content 17 | * dashboard with case list 18 | * real-time collaboration 19 | * detect changes in CAD 20 | * git history in the UX 21 | * show face id when hovering 22 | * screenshots 23 | * once a minute refresh the axes, faces, edges, etc. (take a snapshot?) 24 | * investigate defeature operation in OCC through Gmsh (would we need a separate UX?) 25 | * re-implement how to show SunCAE version in about (when running `deps.sh`) 26 | * check that everything is fine when running `deps.sh`: 27 | - that executables work 28 | - that permissions are fine 29 | - create a `txt` with versions with `git log -1` + `git status --porcelain` 30 | * ability to take notes in markdown 31 | * help ballons, markdown + pandoc 32 | * limit DOFs: in conf? somewhere in auth? like `limits.php`? 33 | * remove visibility, everything is public 34 | 35 | 36 | ## Gmsh 37 | 38 | * STL input 39 | * combos for algorithms 40 | * checkboxes for bool 41 | * local refinements 42 | * understand failures -> train AI to come up with a proper `.geo` 43 | * other meshers! (tetget? netgen?) 44 | * multi-solid: bonded, glued, contact 45 | * curved tetrahedra 46 | * hexahedra 47 | 48 | ## Problem 49 | 50 | * choose faces with ranges e.g 1-74 51 | * other problems: modal 52 | * other solvers: ccx, sparselizard 53 | * orthotropic elasticity 54 | * thermal expansion (isotropic and orthotropic) 55 | * modal feenox 56 | * mechanical sparselizard 57 | * transient/quasistatic (a slider for time?) 58 | 59 | ## Results 60 | 61 | * fields (the list of available fields should be read from the outpt vtk/msh) 62 | - heat flux? (more general, vector fields?) 63 | * the server should tell the client 64 | - which field it is returning (so the client can choose the pallete) 65 | - if it has a warped field or not 66 | * the range scale has to be below the warp slider 67 | * nan color (yellow) 68 | * compute the .dat in the PHP, not in Bash 69 | * probes: user picks location, server returns all field 70 | * reactions: choose which BCs to compute reaction at in the problem step with a checkboxes 71 | * warning for large deformations/stresses 72 | 73 | ## Outer loops 74 | 75 | * parametric 76 | * optimization 77 | 78 | ## Dashboard 79 | 80 | * list of cases 81 | 82 | ## Backlog 83 | 84 | * zoom over mouse 85 | * disable BCs (comment them out) 86 | -------------------------------------------------------------------------------- /auths/htpasswd/.htaccess: -------------------------------------------------------------------------------- 1 | AuthType Basic 2 | AuthName "Restricted Content" 3 | AuthUserFile .htpasswd 4 | Require valid-user 5 | -------------------------------------------------------------------------------- /auths/htpasswd/.htpasswd: -------------------------------------------------------------------------------- 1 | pepe:$apr1$vATa7bcq$EBEVaET9ke0EWE6zs4P4Q1 2 | -------------------------------------------------------------------------------- /auths/htpasswd/README.md: -------------------------------------------------------------------------------- 1 | Explain how to setup httpasswd. 2 | -------------------------------------------------------------------------------- /auths/htpasswd/auth.php: -------------------------------------------------------------------------------- 1 | &1", $output, $error_level); 21 | 22 | // TODO: keep output 23 | if ($error_level != 0) { 24 | $response["status"] = "error"; 25 | $response["error"] = "Unknown yyy error {$error_level} when importing CAD."; 26 | for ($i = 0; $i < count($output); $i++) { 27 | $response["error"] .= $output[$i]; 28 | } 29 | suncae_log("CAD {$cad_hash} process {$response["status"]} {$response["error"]}"); 30 | return_back_json($response); 31 | } 32 | } 33 | 34 | // ------------------------------------------------------------ 35 | if (file_exists("cad.json")) { 36 | $cad = json_decode(file_get_contents("cad.json"), true); 37 | $response["position"] = $cad["position"]; 38 | $response["orientation"] = $cad["orientation"]; 39 | $response["centerOfRotation"] = $cad["centerOfRotation"]; 40 | $response["fieldOfView"] = $cad["fieldOfView"]; 41 | 42 | } else { 43 | $response["status"] = "error"; 44 | $response["error"] = "Cannot create CAD json."; 45 | suncae_log("CAd {$cad_hash} process {$response["status"]} {$response["error"]}"); 46 | return_back_json($response); 47 | } 48 | 49 | 50 | // ------------------------------------------------------------ 51 | // leave running the mesher in the background 52 | exec("../../../../cadprocessors/gmsh/initial_mesh.sh > cadmesh.log 2>&1 &"); 53 | 54 | suncae_log("CAD {$cad_hash} process {$response["status"]} {$response["error"]}"); 55 | 56 | 57 | return_back_json($response); 58 | 59 | -------------------------------------------------------------------------------- /conf.php: -------------------------------------------------------------------------------- 1 | [!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 | ### What are the licensing terms? 23 | 24 | See the [licensing terms](licensing.md). 25 | 26 | ### What are the supported meshers? 27 | 28 | So far, only [Gmsh](http://gmsh.info/) is supported. 29 | 30 | See the directory [`meshers`](https://github.com/seamplex/suncae/tree/main/meshers) for the current list. 31 | 32 | If you want other meshers to be supported, say so in the [forum](https://github.com/seamplex/suncae/discussions). 33 | 34 | 35 | ### What are the supported solvers? 36 | 37 | So far, only [FeenoX](http://www.seamplex.com/feenox) is supported. 38 | 39 | See the directory [`solvers`](https://github.com/seamplex/suncae/tree/main/solvers) for the current list. 40 | 41 | If you want other solvers to be supported, say so in the [forum](https://github.com/seamplex/suncae/discussions). 42 | 43 | -------------------------------------------------------------------------------- /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 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` and `feenox`) 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 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 directorty `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 now 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 | $mesher = "gmsh"; 142 | $post = "paraview"; 143 | $runner = "local"; 144 | $solver = "feenox"; 145 | $mesher = "gmsh"; 146 | ``` 147 | 148 | This means that 149 | 150 | 1. the same server can change the implementations by changing the content of `conf.php` dynamically 151 | 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. 152 | 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). 153 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # SunCAE documentation 2 | 3 | * [Installation guide](INSTALL.md) 4 | * [Tutorials](tutorials.md) 5 | * [FAQs](FAQs.md) 6 | * [Licensing](licensing.md) 7 | 8 | -------------------------------------------------------------------------------- /doc/licenses.md: -------------------------------------------------------------------------------- 1 | # Renderers 2 | 3 | MIT/GPLv3+ 4 | 5 | # Meshers 6 | 7 | ## Gmsh 8 | 9 | GPLv2+ 10 | 11 | # Solvers 12 | 13 | ## Feenox 14 | 15 | GPLv3+ 16 | 17 | ## Sparselizard 18 | 19 | GPLv2+ 20 | 21 | ## CalculiX 22 | 23 | GPLv2+ 24 | 25 | -------------------------------------------------------------------------------- /doc/licensing.md: -------------------------------------------------------------------------------- 1 | # Licensing 2 | 3 | [SunCAE](https://www.seamplex.com/suncae) is released 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. This means that you get the four essential freedoms, so you can 4 | 5 | 0. **Run** SunCAE as you seem fit (i.e. for any purpose). 6 | 1. **Investigate** the source code to see how 7 | SunCAE works and to change it (or to hire someone to change it four 8 | you) as you seem fit (i.e. to suit your needs) 9 | 2. **Redistribute** copies of the source code as 10 | you seem fit (i.e. to help your neighbor) 11 | 3. **Publish** your changes (or the ones that 12 | the people you hired made) to the public (i.e. to benefit the 13 | community). 14 | 15 | If you use a *modified* version of SunCAE in your web server, [section 13 of the AGPL license](https://www.gnu.org/licenses/agpl-3.0.en.html#section13) requires you to give a link where your users can get these four freedoms as well. That is to say, if you use a verbatim copy of SunCAE in your server, there is nothing for you to do (because the link is already 16 | provided). But if you exercise freedoms 1 & 3 above and *modify* SunCAE to suit your needs---let\'s say you don\'t like the button \"Add Boundary Condition\" and you change it to \"Add restrains and loads\"---you do need to provide a link for 17 | people to download the modified source code. 18 | 19 | > [!NOTE] 20 | > If this licensing scheme does not suit you, contact us to see how we can 21 | make it work. For example, 22 | > 23 | > - If you have a solver released under a license which is compatible 24 | > with the AGPL and you would like to add it to SunCAE, feel free to 25 | > fork the repository (and create a pull request when you are done). 26 | > - If you have a solver released under a license which is not 27 | > compatible with the AGPL and you would like to add it to SunCAE, 28 | > [contact us](https://www.seamplex.com/suncae#contact). 29 | 30 | 31 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /html/.htaccess: -------------------------------------------------------------------------------- 1 | # AuthType Basic 2 | # AuthName "Restricted Content" 3 | # AuthUserFile /home/gtheler/codigos/suncae/auths/htpasswd/.htpasswd 4 | # Require valid-user 5 | -------------------------------------------------------------------------------- /html/ajax2mesh.php: -------------------------------------------------------------------------------- 1 | 107 | -------------------------------------------------------------------------------- /html/change_step.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 | if (file_exists("../../../logs") == false) { 53 | if (mkdir("../../../logs", $permissions, true) == false) { 54 | echo "error: cannot create log directory"; 55 | exit(); 56 | } 57 | } 58 | $log = fopen("../../../logs/".date("Y-m-d").".log", "a"); 59 | if ($log === false) { 60 | echo "Cannot open log file, please check permissions."; 61 | exit(1); 62 | } 63 | fprintf($log, "%s %s\t%s: %s\n", date("c"), $_SERVER['REMOTE_ADDR'], $username, $message); 64 | fclose($log); 65 | } 66 | -------------------------------------------------------------------------------- /html/css/faster-than-quick: -------------------------------------------------------------------------------- 1 | ../../uxs/faster-than-quick/css/ -------------------------------------------------------------------------------- /html/expert.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 | -------------------------------------------------------------------------------- /meshers/gmsh/cadmesh.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import os 3 | import sys 4 | import json 5 | 6 | 7 | def mesh(attempt): 8 | os.system("timeout 30 ../../../../meshers/gmsh/trymesh.py %d > 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/default0.geo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seamplex/suncae/103134bf944bebf9c5ecdb6113ec2040a9d4aaa3/meshers/gmsh/default0.geo -------------------------------------------------------------------------------- /meshers/gmsh/default1.geo: -------------------------------------------------------------------------------- 1 | Mesh.MeshSizeFromCurvature = 5; 2 | -------------------------------------------------------------------------------- /meshers/gmsh/default2.geo: -------------------------------------------------------------------------------- 1 | Mesh.MeshSizeFromCurvature = 10; 2 | Mesh.Algorithm = 1; 3 | -------------------------------------------------------------------------------- /meshers/gmsh/default3.geo: -------------------------------------------------------------------------------- 1 | Mesh.MeshSizeFromCurvature = 12; 2 | Mesh.AlgorithmSwitchOnFailure = 1; 3 | Mesh.Algorithm = 2; 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/false 2 | 3 | gmsh_version=4.13.1 4 | 5 | # gmsh 6 | echo -n "meshers/gmsh... " 7 | # gmsh_tarball=gmsh-nox-git-Linux64-sdk 8 | gmsh_tarball=gmsh-${gmsh_version}-Linux64-sdk 9 | if [ $force = 1 ] || [ ! -x bin/gmsh ] || [ ! -f deps/${gmsh_tarball}.tgz ]; then 10 | cd deps 11 | if [ ! -e ${gmsh_tarball}.tgz ]; then 12 | wget -c http://gmsh.info/bin/Linux/${gmsh_tarball}.tgz 13 | fi 14 | tar xzf ${gmsh_tarball}.tgz 15 | cp ${gmsh_tarball}/bin/gmsh ../bin 16 | cp ${gmsh_tarball}/lib/gmsh.py ../bin 17 | cp -d ${gmsh_tarball}/lib/libgmsh.so* ../bin 18 | cd ../bin 19 | # this is needed to add pwd to the binary's rpath 20 | patchelf --set-rpath ${PWD} gmsh 21 | echo "done" 22 | cd .. 23 | else 24 | echo "already installed" 25 | fi 26 | -------------------------------------------------------------------------------- /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 | 45 | # we can have a partial mesh, though 46 | # TODO: rewrite mesh_data in C++ 47 | if [ -e ${dir}/meshes/${mesh_hash}.msh ]; then 48 | ../../../../meshers/gmsh/mesh_data.py ${mesh_hash} ${dir}/meshes > ${dir}/meshes/${mesh_hash}-data.log 49 | fi 50 | 51 | # the metadata depends on whether the mesh worked or not 52 | ../../../../meshers/gmsh/mesh_meta.py ${dir}/meshes/${mesh_hash} ${gmsh_error} > ${dir}/meshes/${mesh_hash}.json 53 | if [ -e ${dir}/meshes/${mesh_hash}.gp ]; then 54 | # TODO: bin 55 | gnuplot ${dir}/meshes/${mesh_hash}.gp 56 | fi 57 | # TODO: should we remove this guy? 58 | # rm -f ${dir}/meshes/${mesh_hash}-status.json 59 | 60 | else 61 | cat << EOF > ${dir}/meshes/${mesh_hash}.json 62 | { 63 | "status": "syntax_error" 64 | } 65 | EOF 66 | fi 67 | 68 | # sync run/meshes/${mesh_hash}.json 69 | rm -f ${dir}/${mesh_hash}.pid 70 | -------------------------------------------------------------------------------- /meshers/gmsh/mesh_data.php: -------------------------------------------------------------------------------- 1 | 86 | mapped_tupple = set(map(tuple,lines)) 87 | lines = map(list,mapped_tupple) 88 | 89 | n_lines = len(mapped_tupple) 90 | k = 0 91 | for line in lines: 92 | if (len(line) == 2): 93 | mesh["surfaces_edges_set"] += "{:d} {:d} -1 ".format(line[0], line[1]) 94 | elif (len(line) == 3): 95 | mesh["surfaces_edges_set"] += "{:d} {:d} {:d} -1 ".format(line[0], line[1], line[2]) 96 | k += 1 97 | print("3", flush=True) 98 | 99 | 100 | # surface faces, one per each physical group ------------ 101 | mesh["surfaces_faces_set"] = {} 102 | physicals = gmsh.model.getPhysicalGroups() 103 | k = 0 104 | for physical in physicals: 105 | dim = physical[0] 106 | physical_tag = physical[1] 107 | if (dim == 2): 108 | mesh["surfaces_faces_set"][physical_tag] = "" 109 | for entity in gmsh.model.getEntitiesForPhysicalGroup(dim, physical_tag): 110 | types, tags, nodetags = gmsh.model.mesh.getElements(dim, entity) 111 | for i in range(len(types)): 112 | for j in range(len(tags[i])): 113 | if types[i] == 2 or (curved == 0 and types[i] == 9): 114 | N = 6 if (types[i] == 9) else 3 115 | # for triangles remove the last int and the -1 116 | 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) 117 | elif types[i] == 9: 118 | N = 6 119 | 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) 120 | 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) 121 | 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) 122 | 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) 123 | k += 1 124 | 125 | print("4", flush=True) 126 | gmsh.finalize() 127 | 128 | 129 | with open("%s/%s-data.json" % (sys.argv[2], sys.argv[1]), "w", encoding ='utf8') as json_file: 130 | json.dump(mesh, json_file) 131 | -------------------------------------------------------------------------------- /meshers/gmsh/mesh_graph.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 | -------------------------------------------------------------------------------- /meshers/gmsh/mesh_inp_show.php: -------------------------------------------------------------------------------- 1 | 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 | -------------------------------------------------------------------------------- /meshers/gmsh/meshing_cancel.php: -------------------------------------------------------------------------------- 1 | 62 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /renderers/x3dom/.gitignore: -------------------------------------------------------------------------------- 1 | x3dom.css 2 | x3dom.js 3 | -------------------------------------------------------------------------------- /renderers/x3dom/LICENSE: -------------------------------------------------------------------------------- 1 | MIT/GPLv3+ -------------------------------------------------------------------------------- /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/README.md: -------------------------------------------------------------------------------- 1 | # Solvers 2 | 3 | * [FeenoX](https://www.seamplex.com/feenox) (GPLv3+) 4 | -------------------------------------------------------------------------------- /solvers/ccx/LICENSE: -------------------------------------------------------------------------------- 1 | GPLv2+ -------------------------------------------------------------------------------- /solvers/ccx/ajax2problem.php: -------------------------------------------------------------------------------- 1 | ../feenox/ajax2problem.php -------------------------------------------------------------------------------- /solvers/ccx/change_step_solve.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/ccx/common.php: -------------------------------------------------------------------------------- 1 | 59 | -------------------------------------------------------------------------------- /solvers/ccx/deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/false 2 | 3 | ccx_version=2.22 4 | 5 | # ccx 6 | echo -n "solvers/ccx... " 7 | # the one from http://www.dhondt.de/ccx_2.22.tar.bz2 needs libgfortran4 8 | # so I packed this one as a static binary 9 | ccx_tarball=ccx-${ccx_version}-linux-static 10 | if [ $force = 1 ] || [ ! -x bin/ccx ] || [ ! -f deps/${ccx_tarball}.tar.gz ]; then 11 | cd deps 12 | if [ ! -e ${ccx_tarball}.tar.gz ]; then 13 | wget -c https://www.seamplex.com/suncae/deps/${ccx_tarball}.tar.gz 14 | fi 15 | tar xzf ${ccx_tarball}.tar.gz 16 | cp ${ccx_tarball}/ccx ../bin 17 | echo "done" 18 | cd .. 19 | else 20 | echo "already installed" 21 | fi 22 | -------------------------------------------------------------------------------- /solvers/ccx/frd2vtk.fee: -------------------------------------------------------------------------------- 1 | READ_MESH $1.frd DIM 3 { 2 | READ_FIELD D1 AS displacements1 3 | READ_FIELD D2 AS displacements2 4 | READ_FIELD D3 AS displacements3 5 | # READ_FUNCTION EXX 6 | # READ_FUNCTION EYY 7 | # READ_FUNCTION EZZ 8 | # READ_FUNCTION EXY 9 | # READ_FUNCTION EYZ 10 | # READ_FUNCTION EZX 11 | READ_FUNCTION SXX 12 | READ_FUNCTION SYY 13 | READ_FUNCTION SZZ 14 | READ_FUNCTION SXY 15 | READ_FUNCTION SYZ 16 | READ_FUNCTION SZX 17 | } 18 | 19 | sigma(x,y,z) = sqrt(0.5*((SXX(x,y,z)-SYY(x,y,z))^2 + \ 20 | (SYY(x,y,z)-SZZ(x,y,z))^2 + \ 21 | (SZZ(x,y,z)-SXX(x,y,z))^2 + \ 22 | 6*(SXY(x,y,z)^2 + SYZ(x,y,z)^2 + SZX(x,y,z)^2))) 23 | 24 | WRITE_MESH $1.vtk { 25 | displacements1 displacements2 displacements3 26 | sigma 27 | # EXX EYY EZZ EXY EYZ EZX 28 | # SXX SYY SZZ SXY SYZ SZX 29 | } 30 | -------------------------------------------------------------------------------- /solvers/ccx/input_initial_mechanical.php: -------------------------------------------------------------------------------- 1 | ../feenox/input_initial_mechanical.php -------------------------------------------------------------------------------- /solvers/ccx/problem_fee.php: -------------------------------------------------------------------------------- 1 | ../feenox/problem_fee.php -------------------------------------------------------------------------------- /solvers/ccx/problem_fee_save.php: -------------------------------------------------------------------------------- 1 | ../feenox/problem_fee_save.php -------------------------------------------------------------------------------- /solvers/ccx/problems.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/ccx/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 READ_MESH case.fee | awk '{print $2}' | tail -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 | -------------------------------------------------------------------------------- /solvers/ccx/solving_status.php: -------------------------------------------------------------------------------- 1 | 51 | -------------------------------------------------------------------------------- /solvers/common.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 | -------------------------------------------------------------------------------- /solvers/feenox/common.php: -------------------------------------------------------------------------------- 1 | 64 | -------------------------------------------------------------------------------- /solvers/feenox/deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/false 2 | 3 | feenox_version=1.0.152 4 | 5 | # feenox 6 | echo -n "solvers/feenox... " 7 | feenox_tarball=feenox-${feenox_version}-linux-amd64 8 | if [ $force = 1 ] || [ ! -x bin/feenox ] || [ ! -f deps/${feenox_tarball}.tar.gz ]; then 9 | cd deps 10 | if [ ! -e ${feenox_tarball}.tar.gz ]; then 11 | wget -c https://www.seamplex.com/feenox/dist/linux/${feenox_tarball}.tar.gz 12 | fi 13 | tar xzf ${feenox_tarball}.tar.gz 14 | cp ${feenox_tarball}/bin/feenox ../bin 15 | cp ${feenox_tarball}/bin/fee2ccx ../bin 16 | echo "done" 17 | cd .. 18 | else 19 | echo "already installed" 20 | fi 21 | -------------------------------------------------------------------------------- /solvers/feenox/displacements.fee: -------------------------------------------------------------------------------- 1 | READ_MESH ${1}-1.vtk DIMENSIONS 3 { 2 | READ_FIELD displacements1 AS u 3 | READ_FIELD displacements2 AS v 4 | READ_FIELD displacements3 AS w 5 | READ_FUNCTION sigma 6 | } 7 | 8 | FIND_EXTREMA u(x,y,z)^2+v(x,y,z)^2+w(x,y,z)^2 MAX displ_max2 9 | displ_max = sqrt(displ_max2) 10 | warp_max = if(displ_max > 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 | -------------------------------------------------------------------------------- /solvers/feenox/field.fee: -------------------------------------------------------------------------------- 1 | DEFAULT_ARGUMENT_VALUE 2 sigma 2 | READ_MESH ${1}.vtk DIM 3 READ_FIELD ${2} AS field 3 | 4 | maximum = vecmax(vec_field) 5 | minimum = vecmin(vec_field) 6 | VECTOR normalized[nodes] 7 | normalized[i] = (vec_field[i]-minimum)/(maximum-minimum) 8 | 9 | OUTPUT_FILE max PATH ${1}-max.json 10 | # TODO: implement PRINTF with FILE 11 | PRINT FILE max "\{" 12 | PRINT FILE max " \"max\": " maximum "," 13 | PRINT FILE max " \"min\": " minimum 14 | PRINT FILE max "\}" 15 | 16 | PRINT_VECTOR %.3f normalized 17 | -------------------------------------------------------------------------------- /solvers/feenox/field1.fee: -------------------------------------------------------------------------------- 1 | DEFAULT_ARGUMENT_VALUE 2 sigma 2 | READ_MESH ${1}-1.vtk DIM 3 READ_FIELD ${2} AS field 3 | 4 | maximum = vecmax(vec_field) 5 | VECTOR normalized[nodes] 6 | normalized[i] = vec_field[i]/maximum 7 | 8 | PRINT_VECTOR %.3f normalized 9 | -------------------------------------------------------------------------------- /solvers/feenox/input_initial_heat_conduction.php: -------------------------------------------------------------------------------- 1 | &1", $output, $result); 30 | if ($result != 0) { 31 | $response["status"] = "error"; 32 | for ($i = 0; $i < count($output); $i++) { 33 | // this authorization comes from openmpi 34 | if ($output[$i] != "" && strncasecmp($output[$i], "Authorization", 13) != 0) { 35 | if (strncmp("error", $output[$i], 5) == 0) { 36 | $output_exploded = explode(":", $output[$i]); 37 | for ($j = 3; $j < count($output_exploded); $j++) { 38 | $response["error"] .= $output_exploded[$j] ; 39 | } 40 | $response["error"] .= "
"; 41 | } else { 42 | $response["error"] .= $output[$i] . "
"; 43 | } 44 | } 45 | } 46 | } 47 | 48 | return_back_json($response); 49 | -------------------------------------------------------------------------------- /solvers/feenox/problems.php: -------------------------------------------------------------------------------- 1 | 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 | -------------------------------------------------------------------------------- /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 READ_MESH case.fee | awk '{print $2}' | tail -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 | -------------------------------------------------------------------------------- /solvers/feenox/solving_relaunch.php: -------------------------------------------------------------------------------- 1 | 51 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /solvers/sparselizard/LICENSE: -------------------------------------------------------------------------------- 1 | GPLv2+ -------------------------------------------------------------------------------- /solvers/sparselizard/problems.php: -------------------------------------------------------------------------------- 1 | 2 | 37 | 38 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/bootswatch/.gitignore: -------------------------------------------------------------------------------- 1 | bootswatch 2 | package-lock.json 3 | 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/cad.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/css/.gitignore: -------------------------------------------------------------------------------- 1 | bootstrap-icons.min.css 2 | bootstrap-icons.css 3 | x3dom.css 4 | fonts 5 | katex.min.css 6 | katex.css 7 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/css/ftq.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 1.0rem; 3 | } 4 | 5 | @font-face { 6 | font-family: Carlito; 7 | src: url(fonts/Carlito-Regular.ttf); 8 | } 9 | 10 | @font-face { 11 | font-family: Carlito; 12 | src: url(fonts/Carlito-Bold.ttf); 13 | font-weight: bold; 14 | } 15 | 16 | @font-face { 17 | font-family: Carlito; 18 | src: url(fonts/Carlito-Italic.ttf); 19 | font-style: italic; 20 | } 21 | 22 | @font-face { 23 | font-family: Carlito; 24 | src: url(fonts/Carlito-BoldItalic.ttf); 25 | font-weight: bold; 26 | font-style: italic; 27 | } 28 | 29 | 30 | body { 31 | font-family: Carlito; 32 | } 33 | 34 | /* #cube_canvas { */ 35 | /* position: fixed; */ 36 | /* bottom: 0; */ 37 | /* right·: 0; */ 38 | /* border: 3px solid #73AD21; */ 39 | /* background-color: red; */ 40 | /* } */ 41 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/css/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seamplex/suncae/103134bf944bebf9c5ecdb6113ec2040a9d4aaa3/uxs/faster-than-quick/css/index.html -------------------------------------------------------------------------------- /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/expert.php: -------------------------------------------------------------------------------- 1 | 8 | 9 |
Expert zone
10 |
11 | 12 |
13 |
14 |

15 | 18 |

19 |
20 |
21 | TODO: mesh 22 | 23 | TOD: vtk 24 |
25 |
26 |
27 |
28 |

29 | 32 |

33 |
34 |
35 | TODO: show usage 36 | 37 | TODO: clear 38 |
39 |
40 |
41 |
42 |

43 | 46 |

47 |
48 |
49 | 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 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/importers/upload.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | 48 | 49 |
50 | 53 |
54 | 55 | 56 |
57 |
58 |
59 |
60 | Pick or drag-and-drop a single-solid CAD file in STEP format.
61 | If you do not have one, download a sample STEP file here. 62 |
63 |
64 | 65 |
66 | 72 | 73 | 74 | 75 | 76 | 77 |
78 | 79 |
80 |
81 | 84 |
85 |
86 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/js/.gitignore: -------------------------------------------------------------------------------- 1 | bootstrap*.js 2 | x3dom*.js 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/js/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seamplex/suncae/103134bf944bebf9c5ecdb6113ec2040a9d4aaa3/uxs/faster-than-quick/js/index.html -------------------------------------------------------------------------------- /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+"_pressure"); 27 | } 28 | 29 | function bc_fixture_update(i) { 30 | string = ""; 31 | if (document.getElementById("bc_"+i+"_fixture_u").checked) { 32 | string += "u=0 "; 33 | } 34 | if (document.getElementById("bc_"+i+"_fixture_v").checked) { 35 | string += "v=0 "; 36 | } 37 | if (document.getElementById("bc_"+i+"_fixture_w").checked) { 38 | string += "w=0 "; 39 | } 40 | 41 | ajax2problem("bc_"+i+"_value" , string); 42 | } 43 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/labels/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package.json 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/labels/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | npm install katex 3 | ``` 4 | 5 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/labels/labels.awk: -------------------------------------------------------------------------------- 1 | BEGIN { 2 | print "const katex = require(\"katex\");" 3 | print "console.log(\" ../labels.php 2 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/labels/labels.txt: -------------------------------------------------------------------------------- 1 | u u 2 | v v 3 | w w 4 | p= p= 5 | E= E= 6 | nu= \\nu= 7 | E_x= E_x= 8 | E_y= E_y= 9 | E_z= E_z= 10 | G_x= G_x= 11 | G_y= G_y= 12 | G_z= G_z= 13 | nu_xy= \\nu_{xy}= 14 | nu_xy= \\nu_{xy}= 15 | nu_xy= \\nu_{xy}= 16 | alpha= \\alpha= 17 | alpha_x= \\alpha_x= 18 | alpha_y= \\alpha_y= 19 | alpha_z= \\alpha_z= 20 | mm \\text{mm} 21 | mm2 \\text{mm}^2 22 | mm3 \\text{mm}^3 23 | MPa \\text{MPa} 24 | GPa \\text{GPa} 25 | 1/K \\text{K}^{-1} 26 | maxu \\max{\\|\\mathbf{u}(x,y,z)\\|} 27 | maxsigma \\max{\\sigma(x,y,z)} 28 | xmin x_\\text{min} 29 | xmax x_\\text{max} 30 | ymin y_\\text{min} 31 | ymax y_\\text{max} 32 | zmin z_\\text{min} 33 | zmax z_\\text{max} 34 | T= T= 35 | h= h= 36 | Tref= T_\\text{ref}= 37 | q2= q^{\\prime\\prime}= 38 | K \\text{K} 39 | Wmm-2K \\text{W}\\cdot\\text{mm}^{-2} 40 | Wmm-2K-1 \\text{W}\\cdot\\text{mm}^{-2}\\cdot\\text{K}^{-1} 41 | k= k(x,y,z)= 42 | q3= q^{\\prime\\prime\\prime}(x,y,z)= 43 | Wm-1K-1 \\text{W}\\cdot\\text{m}^{-1}\\cdot\\text{K}^{-1} 44 | Wmm-3 \\text{W}\\cdot\\text{mm}^{-3} 45 | maxT \\max{\\|T(x,y,z)\\|} 46 | minT \\min{\\|T(x,y,z)\\|} 47 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/mesh.php: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 |
Meshing progress
20 |
21 | 22 |
23 | 24 |
25 |

26 | 29 |

30 |
31 |
32 | 33 | 34 | 35 | 1D : : 0/ edges 36 |
37 |
38 |
39 | 40 | 2D : : 0/ faces 41 |
42 |
43 |
44 | 45 | 3D : : 0/ volumes 46 |
47 |
48 |
49 | 50 | Processing 51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 | Meshing... 59 |
60 |
61 |
62 | 63 |
64 |
65 |
66 | 
67 | 
68 | 
69 | 
70 | 
71 | 
72 |
73 | 74 | 77 | 78 |
79 | 80 |
81 |
82 |
83 | 84 | 85 | 86 |
87 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/preview.php: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/problem.php: -------------------------------------------------------------------------------- 1 | 30 | 31 |
32 | 33 |
34 | 38 |
39 |
40 | 41 | 80 | 81 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/results.php: -------------------------------------------------------------------------------- 1 | 19 |
20 | The solving process was canceled. 21 | 22 |
23 | 24 | 27 | 30 |
31 | 35 |
36 | 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 |
62 |
63 | 64 | Download VTK 65 | 66 | 69 |
70 |
71 | 77 |
Not yet implemented
78 | 84 |
Not yet implemented
85 | 96 | 97 |
98 |
99 |
100 | 
101 | 
102 |
103 | 108 | 109 |
110 |
111 | 115 | 116 | 120 |
121 |
122 | 123 |
"> 124 |
125 |
126 | 127 |
128 |
129 | 130 |
131 |
132 | 133 |
134 |
135 |
136 | 137 | 0) { 139 | ?> 140 | 141 | 144 | 145 | 148 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/results/heat_conduction.php: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /uxs/faster-than-quick/results/mechanical.php: -------------------------------------------------------------------------------- 1 | 12 | 13 |
Share case
14 |
15 | 16 |
17 |
18 |

19 | 22 |

23 |
24 |
25 | choose email address 26 |
27 |
28 |
29 |
30 |

31 | 34 |

35 |
36 |
37 | Twitter, 38 | LinkedIn, 39 | etc 40 |
41 |
42 |
43 |
44 | 45 | -------------------------------------------------------------------------------- /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/solving.php: -------------------------------------------------------------------------------- 1 | 8 | 9 |
Solving progress
10 |
11 | 12 |
13 | 14 |
15 |

16 | 19 |

20 |
21 |
22 | 23 | 24 | 25 | ">Second order mesh 26 |
" role="progressbar"> 27 |
28 |
29 | 30 | Build 31 |
32 |
33 |
34 | 35 | Solve 36 |
37 |
38 |
39 | 40 | Compute fluxes 41 |
42 |
43 |
44 | 45 | 51 |
52 |
53 |
54 | Solving... 55 |
56 |
57 |
58 | 59 |
60 | 63 | 64 |
65 | 66 |
67 |
68 |
69 | 70 | 71 | 72 |
73 | --------------------------------------------------------------------------------