diff --git a/.github/workflows/speos-optislang.yml b/.github/workflows/speos-optislang.yml new file mode 100644 index 00000000..0251139d --- /dev/null +++ b/.github/workflows/speos-optislang.yml @@ -0,0 +1,114 @@ +name: Speos Optislang Workflow + +on: + workflow_dispatch: + inputs: + doc-build: + required: false + default: false + type: boolean + description: 'Whether to build the documentation' + workflow_call: + inputs: + doc-build: + required: false + default: false + type: boolean + description: 'Whether to build the documentation' + push: + branches: + - main + pull_request: + paths: + - 'speos-optislang/**' + +env: + MAIN_PYTHON_VERSION: '3.12' + ANSYSLMD_LICENSE_FILE: ${{ format('1055@{0}', secrets.LICENSE_SERVER )}} + ANSYS_RELEASE_FOR_DOCS: 25.2 + RUN_DOC_BUILD: false + AWP_ROOT252: 'C:\Program Files\ANSYS Inc\v252' + +jobs: + + speos-optislang: + name: Speos Optislang + runs-on: [self-hosted, Windows, pyansys-workflows] + strategy: + fail-fast: false + matrix: + ansys-release: [25.2.0] + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + sparse-checkout: | + speos-optislang + doc + + - name: Set up Python ${{ env.MAIN_PYTHON_VERSION }} + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: ${{ env.MAIN_PYTHON_VERSION }} + + - name: Set up environment variables + shell: bash + run: | + VERSION="${{ matrix.ansys-release }}" + ANSYS_RELEASE_SHORT="${VERSION%.*}" + echo "ANSYS_RELEASE_SHORT=$ANSYS_RELEASE_SHORT" >> $GITHUB_ENV + ANSYS_RELEASE_COMPACT="${ANSYS_RELEASE_SHORT//./}" + echo "ANSYS_RELEASE_COMPACT=$ANSYS_RELEASE_COMPACT" >> $GITHUB_ENV + + - name: Install dependencies + shell: bash + run: | + python -m pip install --upgrade pip + python -m venv .venv + .venv/Scripts/activate + pip install -r speos-optislang/requirements_${{ env.ANSYS_RELEASE_SHORT }}.txt + + - name: Run the workflow script + shell: bash + run: | + .venv/Scripts/activate + python speos-optislang/wf_so_01_light_guide_robustness_study.py + + # - name: Store the outputs + # uses: actions/upload-artifact@v4 + # with: + # name: ... + # path: ... + + - name: (DOCS) Check if docs should be built + if: (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && inputs.doc-build + shell: bash + run: | + echo "Requested to build docs..." + if [ "${{ matrix.ansys-release }}" = "$ANSYS_RELEASE_FOR_DOCS" ]; then + echo "Building docs" + echo "RUN_DOC_BUILD=true" >> $GITHUB_ENV + else + echo "Not building docs - since not primary release" + echo "RUN_DOC_BUILD=false" >> $GITHUB_ENV + fi + + - name: (DOCS) Build the documentation (only on ${{ env.ANSYS_RELEASE_FOR_DOCS}}) + if: ${{ env.RUN_DOC_BUILD == 'true' }} + env: + BUILD_DOCS_SCRIPT: 'speos-optislang/wf_so_01_light_guide_robustness_study.py' + shell: bash + run: | + .venv/Scripts/activate + cd doc + pip install -r requirements.txt + ./make.bat html + + - name: (DOCS) Upload docs artifacts + if: ${{ env.RUN_DOC_BUILD == 'true' }} + uses: actions/upload-artifact@v4 + with: + name: speos-optislang-docs-stage + path: | + doc/_build/ + doc/source/examples/speos-optislang/ diff --git a/doc/source/conf.py b/doc/source/conf.py index 95343998..b9a24a9f 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -138,12 +138,14 @@ def examples_gallery_dirs_and_filename_pattern(): "../../geometry-mechanical-dpf", "../../geometry-mesh", "../../geometry-mesh-fluent", + "../../speos-optislang", ] gallery_dirs = [ "examples/fluent-mechanical", "examples/geometry-mechanical-dpf", "examples/geometry-mesh", "examples/geometry-mesh-fluent", + "examples/speos-optislang", ] return examples_dirs, gallery_dirs, filename_pattern diff --git a/speos-optislang/Lightguide.speos/DRL_Upper-only.VE-measure.xml b/speos-optislang/Lightguide.speos/DRL_Upper-only.VE-measure.xml new file mode 100644 index 00000000..c67a4b8c --- /dev/null +++ b/speos-optislang/Lightguide.speos/DRL_Upper-only.VE-measure.xml @@ -0,0 +1,571 @@ + + +2 +2 +0 +"Extracted from file Lightguide.Radiance.1.xmp" + +-30 +30 +600 +-22.5 +22.5 +450 +100 +0.017453292519943295 + +400 +700 +13 +1 +false + + + +true +0 +2 +0 +true +false +false +false +false +false +false +false +false +false +false +false +false +millimetre +%g +true + +false +true +true +true +true +false +6579300 +false +true +true + +2 + +false +false + +true +0 +0 +6 +4.5 +0 +0 +0.10000000000000001 +0.10000000000000001 +9 +9 + +false +false + + + +1 + +PassNumber +2 + + + +false +"" + + +"S" +true +true + + +"Area" +true +true + + +"Shape" +true +true + + +"Center" +false +false + + +"Normalized direction" +false +false + + +"Parameters" +true +true + + +"Magnitude" +true +true + + +"Operator" +true +true + + +"Measure" +true +true + + +"Thresholds" +true +true + + +"Value" +true +true + + +"Standard Deviation" +true +true + + +"Rule" +true +true + + +"Formula" +true +true + + +"Minimum" +true +true + + +"Maximum" +true +true + + +"Margin" +true +true + + +"Confidence Level" +true +true + + +"Minimum Specification" +true +true + + +"Maximum Specification" +true +true + + +"Margin Specification" +true +true + + +"Confidence Level Specification" +true +true + + + + + + +false +"Area_1" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +300 + + +21.102537300826555 +8.1719518914255858 + + + + +19.997393366271368 +8.2204617914812204 + + + + +18.557797977837637 +8.3013116249072798 + + + + +16.434758314086881 +8.398331425018549 + + + + +13.613733007459167 +8.4468413250741854 + + + + +11.258031462749422 +8.3659914916481259 + + + + +9.2804054745980338 +8.2366317581664337 + + + + +7.6663136754450614 +8.0264221912586819 + + + + +5.9504323033725299 +7.7030228575544459 + + + + +3.9873476827810777 +7.2502637903685194 + + + + +2.1115112675492469 +6.7489948231269583 + + + + +0.15826740693958996 +6.039625390769876 + + + + +-2.3817713084711887 +5.0673182878649436 + + + + +-5.202796615098908 +3.8545707864740688 + + + + +-8.3291906404852938 +2.3184239513789588 + + + + +-11.310170990272315 +0.63674741611694508 + + + + +-13.535000226942627 +-0.7053598187556247 + + + + +-15.527167582653952 +-2.0959769536838291 + + + + +-16.64920757044673 +-2.844330950082206 + + + + + + + +0 +0 +15 +false +false +0 +0 +"" +"" +"" +"" +"" +"" +false +false +false +false + + +0 +0 +7 +false +false +0 +0 +"" +"" +"" +"" +"" +"" +false +false +false +false + + +0 +0 +4 +false +false +0 +0 +"" +"" +"" +"" +"" +"" +false +false +false +false + + +0 +0 +1 +false +false +0 +0 +"" +"" +"" +"" +"" +"" +false +false +false +false + + + + +0 +3 + +1 +-1 +0 +false + +false +1059.8573024996633 +0 +1.000000 +false +false +false +0 +1059.8573024996633 +3000 +50 +0 +false + + +20 +0 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + +33023 +54015 +65497 +65414 +65331 +2162432 +7601920 +13041408 +16770560 +16749312 +0 + +false +0 + + + +1117431.625 +1005688.4625 +893945.30000000005 +782202.13749999995 +670458.97499999998 +558715.8125 +446972.64999999991 +335229.48749999993 +223486.32499999995 +111743.16249999998 +0 + + +33023 +54015 +65497 +65414 +65331 +2162432 +7601920 +13041408 +16770560 +16749312 +0 + +false +0 + + + +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 + + +33023 +54015 +65497 +65414 +65331 +2162432 +7601920 +13041408 +16770560 +16749312 +0 + +false +0 + + + +3413.3525390625 +3072.0172851562502 +2730.6820312499999 +2389.3467773437501 +2048.0115234374998 +1706.67626953125 +1365.3410156250002 +1024.0057617187499 +682.67050781250009 +341.33525390625027 +0 + + +33023 +54015 +65497 +65414 +65331 +2162432 +7601920 +13041408 +16770560 +16749312 +0 + +false +0 + + +true +-1 +0 +2 + + +true +1 + + +true +1 + + + +false + + + diff --git a/speos-optislang/Lightguide.speos/ECE_R87_DRL_WithoutLines.xml b/speos-optislang/Lightguide.speos/ECE_R87_DRL_WithoutLines.xml new file mode 100644 index 00000000..599f15dd --- /dev/null +++ b/speos-optislang/Lightguide.speos/ECE_R87_DRL_WithoutLines.xml @@ -0,0 +1,1426 @@ + + +3 +1 +1 +1 +"Extracted from file D:\Tino\02_LG_Headlamp_Regulations_specification\Headlamp_lightguide_demo.opd\Pre-Optimization_for_Regulation\Design0016\SPEOS isolated files\Direct Simulation.Isolated.sv5\1.xmp" + +-0.52359877559829882 +0.52359877559829882 +120 +-0.17453292519943295 +0.26179938779914941 +50 +0 +0 + +1 + + + +false +0 +1 +0 +true +false +false +false +false +true +false +false +false +false +false +false +false +degree +%g +true + +true +true +true +true +true +false +6579300 +false +true +true + +2 + +false +false + +false +0 +0 +0.17453292519943295 +0.17453292519943295 +0 +0 +0.087266462599716474 +0.087266462599716474 +1 +1 + +false +false + + + +1 + +PassNumber +2 + + + +"" + + +"S" +true +true + + +"Area" +true +true + + +"Shape" +false +false + + +"Center" +false +false + + +"Normalized direction" +false +false + + +"Parameters" +false +false + + +"Magnitude" +false +false + + +"Operator" +false +false + + +"Measure" +false +false + + +"Thresholds" +false +false + + +"Value" +true +true + + +"Standard Deviation" +true +true + + +"Rule" +true +true + + +"Formula" +true +true + + +"Minimum" +true +true + + +"Maximum" +true +true + + +"Margin" +true +true + + +"Confidence Level" +true +true + + +"Minimum Specification" +true +true + + +"Maximum Specification" +true +true + + +"Margin Specification" +true +true + + +"Confidence Level Specification" +true +true + + + + + + +false +"Beam_pattern" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0.3490658503988659 +0.174532925199433 +0.3490658503988659 +-0.087266462599716474 + + + + + +0 +0 +4 +false +false +0 +0 +"Beam_pattern_1" +"" +"1" +"" +"" +"" +true +false +false +false + + +0 +0 +1 +false +false +0 +0 +"Beam_pattern_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"5D-20L" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +0.3490658503988659 +-0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5D-20L_1" +"" +"40" +"" +"60" +"" +true +false +true +false + + +0 +0 +7 +false +false +0 +0 +"5D-20L_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"H-20L" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +0.3490658503988659 +0 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"H-20L_1" +"" +"100" +"" +"150" +"" +true +false +true +false + + +0 +0 +7 +false +false +0 +0 +"H-20L_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"5U-20L" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +0.3490658503988659 +0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5U-20L_1" +"" +"40" +"" +"60" +"" +true +false +true +false + + +0 +0 +7 +false +false +0 +0 +"5U-20L_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"5D-10L" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +0.174532925199433 +-0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5D-10L_1" +"" +"80" +"" +"120" +"" +true +false +true +false + + +0 +0 +7 +false +false +0 +0 +"5D-10L_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"H-10L" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +0.174532925199433 +0 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"H-10L_1" +"" +"280" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"H-10L_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"5U-10L" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +0.174532925199433 +0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5U-10L_1" +"" +"80" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"5U-10L_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"H-5L" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +0.087266462599716474 +0 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"H-5L_1" +"" +"360" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"H-5L_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"10U-5L" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +0.087266462599716474 +0.174532925199433 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"10U-5L_1" +"" +"80" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"10U-5L_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"5D-V" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0 +-0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5D-V_1" +"" +"280" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"5D-V_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"HV" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0 +0 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"HV_1" +"" +"400" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"HV_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"5U-V" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0 +0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5U-V_1" +"" +"280" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"5U-V_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"10U-V" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0 +0.174532925199433 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"10U-V_1" +"" +"80" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"10U-V_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"H-5R" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0.087266462599716474 +0 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"H-5R_1" +"" +"360" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"H-5R_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"10U-5R" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0.087266462599716474 +0.174532925199433 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"10U-5R_1" +"" +"80" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"10U-5R_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"5D-10R" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0.174532925199433 +-0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5D-10R_1" +"" +"80" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"5D-10R_2" +"" +"" +"1200" +"" +"1000" +false +true +false +true + + + + + + +false +"H-10R" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0.174532925199433 +0 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"H-10R_1" +"" +"280" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"H-10R_2" +"" +"" +"1200" +"" +"900" +false +true +false +true + + + + + + +false +"5U-10R" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0.174532925199433 +0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5U-10R_1" +"" +"80" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"5U-10R_2" +"" +"" +"1200" +"" +"900" +false +true +false +true + + + + + + +false +"5D-20R" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0.3490658503988659 +-0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5D-20R_1" +"" +"40" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"5D-20R_2" +"" +"" +"1200" +"" +"900" +false +true +false +true + + + + + + +false +"H-20R" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0.3490658503988659 +0 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"H-20R_1" +"" +"100" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"H-20R_2" +"" +"" +"1200" +"" +"900" +false +true +false +true + + + + + + +false +"5U-20R" +1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 0.000000e+00 0.000000e+00 0.000000e+00 0.000000e+00 1.000000 + +10000 +-0.3490658503988659 +0.087266462599716474 +0.0043633231299858239 +0.0043633231299858239 + + + + + +0 +0 +7 +false +false +0 +0 +"5U-20R_1" +"" +"40" +"" +"" +"" +true +false +false +false + + +0 +0 +7 +false +false +0 +0 +"5U-20R_2" +"" +"" +"1200" +"" +"900" +false +true +false +true + + + + +20 +1 + + + +1200 +1080 +960 +840 +720 +600 +480 +360 +240 +120 +0 + + +33023 +54015 +65497 +65414 +65331 +2162432 +7601920 +13041408 +16770560 +16749312 +0 + +false +0 + +1 + +true +-1 +0 +2 + + +true +1 + + +true +1 + + + + + + diff --git a/speos-optislang/Lightguide.speos/Lightguide.speos b/speos-optislang/Lightguide.speos/Lightguide.speos new file mode 100644 index 00000000..0d1e7459 Binary files /dev/null and b/speos-optislang/Lightguide.speos/Lightguide.speos differ diff --git a/speos-optislang/requirements_25.2.txt b/speos-optislang/requirements_25.2.txt new file mode 100644 index 00000000..6fec97e8 --- /dev/null +++ b/speos-optislang/requirements_25.2.txt @@ -0,0 +1,2 @@ +ansys-speos-core[graphics]==0.5.1 +ansys-optislang-core==1.0.0 \ No newline at end of file diff --git a/speos-optislang/wf_so_01_light_guide_robustness_study.py b/speos-optislang/wf_so_01_light_guide_robustness_study.py new file mode 100644 index 00000000..fc022367 --- /dev/null +++ b/speos-optislang/wf_so_01_light_guide_robustness_study.py @@ -0,0 +1,730 @@ +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +""" +.. _ref_speops-optislang-01-light-guide-robustness-study: + +Evaluating robustness of exterior lightguide +############################################ + +In this example, we use Speos and optiSLang to analyze the robustness of a prismatic light +guide and quantify the failure rate due to production tolerances in an automated way. +We will understand which tolerances lead to regulation fails for the automotive +Day-Time-Running lights and which tolerances must be improved to increase the optical +performance. Additionally, we will evaluate the scattering of the homogeneity (RMS-contrast) +and the lit appearance (average luminance) to see what the worst design looks like due to +tolerances. + +""" # noqa: D400, D415 + +import os +import pathlib +import time + +from ansys.optislang.core import Optislang +import ansys.optislang.core.node_types as node_types +from ansys.optislang.core.nodes import DesignFlow +from ansys.optislang.core.project_parametric import ( + ComparisonType, + ConstraintCriterion, + DistributionType, + ObjectiveCriterion, + StochasticParameter, +) +import ansys.speos.core as core +from ansys.speos.core import Body, Project +from ansys.speos.core.simulation import SimulationDirect +from ansys.speos.core.source import SourceSurface +from comtypes.client import CreateObject +import numpy as np + +############################################################################### +# Parameters for the script +# ------------------------- +# The following parameters are used to control the script execution. You can +# modify these parameters to suit your needs. +# + +MAXIMUM_NUMBER_SIMULATIONS: int = 200 +"""Maximum number of simulations for the robustness analysis.""" + +PARAMETERS: list[dict] = [ + { + "name": "LED_delta_x", + "value": 0.0, + "distribution_type": "NORMAL", + "distribution_parameters": [0, 0.25], # mean value, standard deviation + }, + { + "name": "LED_delta_y", + "value": 0.0, + "distribution_type": "NORMAL", + "distribution_parameters": [0, 0.25], # mean value, standard deviation + }, + { + "name": "LED_delta_z", + "value": 0.0, + "distribution_type": "NORMAL", + "distribution_parameters": [0, 0.25], # mean value, standard deviation + }, + { + "name": "Flux", + "value": 200.0, + "distribution_type": "TRUNCATEDNORMAL", + "distribution_parameters": [ + 300, + 45, + 200, + 400, + ], # mean value, standard deviation, lower bound, upper bound + }, +] +"""Input parameters and their statistical distributions.""" + +RESPONSES: list[dict] = [ + {"name": "RMS_contrast", "value": 1.0}, + {"name": "Average", "value": 120.0}, + {"name": "Number_of_rules_limited_passed", "value": 0.0}, + {"name": "Number_of_rules_failed", "value": 0.0}, +] +"""Expected responses in the robustness analysis.""" + +CRITERIA: list[dict] = [ + {"name": "RMS_contrast", "type": "constraint", "limit": 0.2, "target": "<="}, + {"name": "Average", "type": "constraint", "limit": 160000, "target": ">="}, + { + "name": "Number_of_rules_limited_passed", + "type": "constraint", + "limit": 2, + "target": "<=", + }, + {"name": "Number_of_rules_failed", "type": "constraint", "limit": 0, "target": "<="}, +] +"""Evaluation criteria for the robustness analysis.""" + +VARIATION_ANALYSIS_BOUNDARIES = { + "maximum_number_simulations": MAXIMUM_NUMBER_SIMULATIONS, + "parameters": PARAMETERS, + "responses": RESPONSES, + "criteria": CRITERIA, +} +"""Variation analysis boundaries for the robustness study.""" + +############################################################################### +# Define functions +# ---------------- +# The following functions are defined to perform various tasks in the +# robustness analysis workflow, such as cleaning databases, displacing faces and bodies, +# opening results, changing source positions and powers, running Speos simulations, +# and creating the optiSLang workflow. + + +def clean_all_dbs(speos_client: core.kernel.client.SpeosClient): + """ + Clean the database info loaded inside a client. + + Parameters + ---------- + speos_client: core.SpeosClient + + Returns + ------- + + """ + for item in ( + speos_client.jobs().list() + + speos_client.scenes().list() + + speos_client.simulation_templates().list() + + speos_client.sensor_templates().list() + + speos_client.source_templates().list() + + speos_client.intensity_templates().list() + + speos_client.spectrums().list() + + speos_client.vop_templates().list() + + speos_client.sop_templates().list() + + speos_client.parts().list() + + speos_client.bodies().list() + + speos_client.faces().list() + ): + item.delete() + + +def displace_face(edit_face, face_name, xyz=[0, 0, 0]): + """ + Move a face in a translational direction defined by a provided vector xyz. + + Parameters + ---------- + edit_face: ansys.speos.core.face.Face + face_name: str + geo_path of face + xyz: tuple + A tuple with 3 elements defining a vector defined by xyz + + Returns + ------- + + """ + temp_face_vertices = edit_face._face.vertices + temp_face_vertices = np.array(temp_face_vertices).reshape((-1, 3)) + new_face_vertices = [np.add(item, xyz) for item in temp_face_vertices] + new_face_vertices = np.array(new_face_vertices).reshape((1, -1)).tolist()[0] + + edit_face.set_vertices(new_face_vertices) + edit_face.commit() + + +def displace_body(project, body_name, xyz=[1, 0, 0]): + """ + Move a body in a translational direction defined by a provided vector xyz. + + Parameters + ---------- + project: script.Project + body_name: str + geo_path of a body + xyz: tuple + A tuple with 3 elements defining a vector defined by xyz + + Returns + ------- + + """ + print(body_name) + edit_body = project.find(name=body_name, name_regex=True, feature_type=Body)[0] + faces = edit_body._geom_features + for face in faces: + face_name = face._name + displace_face(face, face_name="/".join([body_name, face_name]), xyz=xyz) + + +def open_result(file): + """ + Open result file and extract results. + + Parameters + ---------- + file: str + file directory + + Returns + ------- + dict + a dictionary with results + + """ + dpf_instance = CreateObject("XMPViewer.Application") + dpf_instance.OpenFile(file) + temp_dir = os.getenv("TEMP") + if not os.path.exists(temp_dir): + os.mkdir(temp_dir) + if "radiance" in file.lower(): + dpf_instance.ImportTemplate( + os.path.join(os.path.abspath(""), "Lightguide.speos", "DRL_Upper-only.VE-measure.xml"), + 1, + 1, + 0, + ) + export_dir = os.path.join(temp_dir, "lg_robustness_result.txt") + dpf_instance.MeasuresExportTXT(export_dir) + file = open(export_dir) + content = file.readlines() + RMS_content = content[10] + Average_content = content[11] + res = { + "RMS_contrast": float( + RMS_content[RMS_content.find("\tValue=") + 7 : RMS_content.find(r"ValueUnit=")] + ), + "Average": float( + Average_content[ + Average_content.find("\tValue=") + 7 : Average_content.find(r"ValueUnit=") + ] + ), + } + return res + else: + dpf_instance.ImportTemplate( + os.path.join(os.path.abspath(""), "Lightguide.speos", "ECE_R87_DRL_WithoutLines.xml"), + 1, + 1, + 0, + ) + export_dir = os.path.join(temp_dir, f"lg_robustness_result.txt") + dpf_instance.MeasuresExportTXT(export_dir) + limited_passed_count = 0 + failed_count = 0 + passed_count = 0 + with open(export_dir) as result_file: + for line in result_file: + if "RuleStatus" in line: + if "(specification failed)" in line: + limited_passed_count += 1 + elif "(passed)" in line: + passed_count += 1 + elif "(failed)" in line: + failed_count += 1 + else: + print("Rules status is unknown.") + + res = { + "Number_of_rules_limited_passed": limited_passed_count, + "Number_of_rules_failed": failed_count, + } + return res + + +def change_surface_source_position(project, sources, source_position_dict): + """ + change the position of surface where surface source is linked. + + Parameters + ---------- + project: script.Project + project in which source will be modified. + sources: script.Source + sources inside the project. + source_position_dict: dict + A dictionary within which position information is saved. + + Returns + ------- + + """ + for source in sources: + source_name = source.get(key="name") + source_geo_path = source.get(key="geo_path")[0] + source_linked_body = source_geo_path["geo_path"].split("/")[0] + if source_name in source_position_dict: + displace_body( + project, + "/".join([source_linked_body]), + source_position_dict[source_name], + ) + + +def change_source_power(sources, source_power_dict): + """ + Change the power of surface where surface source is linked. + + Parameters + ---------- + sources: script.Source + sources inside the project. + source_power_dict: dict + A dictionary within which source power information is saved. + + Returns + ------- + + """ + for source in sources: + source_name = source.get(key="name") + if source_name in source_power_dict: + source.set_flux_luminous(value=source_power_dict[source_name]) + source.commit() + + +def speos_simulation(hid, speos, parameters): + """ + Run speos simulation with given source parameters to be changed. + + Parameters + ---------- + hid: str + string that contains the design id + speos: core.Speos + parameters: dict + dictionary includes parameters to be used for each design iteration + + Returns + ------- + + """ + new_parameter_values = {p["name"]: p["value"] for p in parameters} + + clean_all_dbs(speos.client) + script_folder = pathlib.Path(__file__).resolve().parent + speos_file = script_folder / "Lightguide.speos" / "Lightguide.speos.speos" + project = Project(speos=speos, path=str(speos_file)) + # project.preview() + + # Update of the light source power + sources = project.find(name=".*", name_regex=True, feature_type=SourceSurface) + new_sources_power = { + "Surface.1:6015": new_parameter_values.get("Flux"), + "Surface.2:30": new_parameter_values.get("Flux"), + } + + change_source_power(sources, new_sources_power) + + # Update of the source position + new_source_displacement = { + "Surface.1:6015": [ + new_parameter_values.get("LED_delta_x"), + new_parameter_values.get("LED_delta_y"), + new_parameter_values.get("LED_delta_z"), + ], + "Surface.2:30": [ + new_parameter_values.get("LED_delta_x"), + new_parameter_values.get("LED_delta_y"), + new_parameter_values.get("LED_delta_z"), + ], + } + change_surface_source_position(project, sources, new_source_displacement) + # project.preview() + + # execution of the Speos simulation + sim = project.find(name=".*", name_regex=True, feature_type=SimulationDirect)[0] + sim.set_stop_condition_rays_number(2000000).commit() + res = sim.compute_CPU() + + # Result extraction + xmp_files = [item.path for item in res if ".xmp" in item.path] + response = {} + result_design = {} + for xmp in xmp_files: + response.update(open_result(xmp)) + result_design.update( + hid=hid, + responses=[ + {"name": "RMS_contrast", "value": response.get("RMS_contrast")}, + {"name": "Average", "value": response.get("Average")}, + { + "name": "Number_of_rules_limited_passed", + "value": response.get("Number_of_rules_limited_passed"), + }, + {"name": "Number_of_rules_failed", "value": response.get("Number_of_rules_failed")}, + ], + ) + result_print = str("Design " + result_design["hid"]) + ": " + for i in range(0, len(result_design["responses"])): + result_print += ( + str(result_design["responses"][i]["name"]) + + " = " + + str(result_design["responses"][i]["value"]) + + " | " + ) + print(result_print) + return result_design + + +def get_executable(version): + """Returns the optiSLang executable for given version. + + Parameters + ---------- + version : int + optiSLang version, e.g. 251 + + Returns + ------- + pathlib.Path + Executable path. + """ + if os.getenv(f"AWP_ROOT{version}"): + awp_root = os.getenv(f"AWP_ROOT{version}") + osl_com = pathlib.Path(awp_root) / "optiSLang" / "optislang.com" + else: + raise Exception(f"optiSLang installation not found. Please install optiSLang v{version}.") + return osl_com + + +def create_workflow(osl, user_input_json): + """ + This function creates the robustness workflow + with a proxy node and register parameters and responses. + + Parameters + ---------- + osl: ansys.optislang.core + Ansys optiSLang instance + user_input_json: dict + dictionary with optimization boundaries as json + + Returns + ------- + ansys.optislang.core.nodes + optiSLang Optimization system and proxy solver node. + """ + # create system + root_system = osl.application.project.root_system + robustness_system = root_system.create_node(type_=node_types.Robustness, name="Robustness") + + # create node + proxy_solver_node = robustness_system.create_node( + type_=node_types.ProxySolver, name="Proxy Solver", design_flow=DesignFlow.RECEIVE_SEND + ) + mop_node = root_system.create_node(type_=node_types.Mop, name="MOP") + + # connect system with MOP node + robustness_system_out = robustness_system.get_output_slots(name="OMDBPath")[0] + mop_node_in = mop_node.get_input_slots(name="IMDBPath")[0] + robustness_system_out.connect_to(mop_node_in) + + # register parameter and responses + proxy_solver_node.load(user_input_json) + proxy_solver_node.register_locations_as_parameter() + proxy_solver_node.register_locations_as_response() + + # adjust parameter ranges + for parameter in user_input_json.get("parameters"): + robustness_system.parameter_manager.modify_parameter( + StochasticParameter( + name=parameter.get("name"), + reference_value=parameter.get("value"), + distribution_type=getattr(DistributionType, parameter.get("distribution_type")), + distribution_parameters=parameter.get("distribution_parameters"), + ) + ) + + # robustness system settings + robustness_settings = robustness_system.get_property("AlgorithmSettings") + robustness_settings["num_discretization"] = user_input_json.get("maximum_number_simulations") + robustness_system.set_property("AlgorithmSettings", robustness_settings) + + # proxy node settings + multi_design_launch_num = 30 # set -1 to solve all designs simultaneously + proxy_solver_node.set_property("MultiDesignLaunchNum", multi_design_launch_num) + proxy_solver_node.set_property("ForwardHPCLicenseContextEnvironment", True) + + return robustness_system, proxy_solver_node + + +def criteria_definition(system, load_json): + """ + This functions defines the robustness criteria in optiSLang based on user input + Parameters + ---------- + system: ansys.optislang.core.nodes + optiSLang Optimization system + load_json: dict + dictionary with optimization boundaries as json + + Returns + ------- + + """ + for crit in load_json.get("criteria"): + crit_name = crit.get("name") + if crit.get("type") == "objective": + if crit.get("target") == "MIN": + comparison_type_obj = ComparisonType.MIN + elif crit.get("target") == "MAX": + comparison_type_obj = ComparisonType.MAX + else: + raise Exception("objective type not defined") + + system.criteria_manager.add_criterion( + ObjectiveCriterion( + name=f"obj_{crit_name}", expression=crit_name, criterion=comparison_type_obj + ) + ) + + if crit.get("type") == "constraint": + if crit.get("target") == "<=": + comparison_type_constr = ComparisonType.LESSEQUAL + elif crit.get("target") == ">=": + comparison_type_constr = ComparisonType.GREATEREQUAL + else: + raise Exception("Constraint type not well defined") + system.criteria_manager.add_criterion( + ConstraintCriterion( + name=f"constr_{crit_name}", + expression=crit_name, + criterion=comparison_type_constr, + limit_expression=str(crit.get("limit")), + ) + ) + + +def calculate(designs): + """ + This function evaluated the outputs based on the given designs parameters. + Parameters + ---------- + designs: list + A list of designs to be evaluated. For each design the parameter values are defined + + Returns + ------- + list + A list of evaluated designs with the corresponding outputs. + """ + # create speos instance + from ansys.speos.core import launcher + + speos = launcher.launch_local_speos_rpc_server(version="252") + + # speos = core.Speos(host="localhost", port=50098) + + # run speos simulation + result_design_list = [] + for design in designs: + hid = design["hid"] + parameters = design["parameters"] + + result_design = speos_simulation(hid, speos, parameters) + result_design_list.append(result_design) + + # close speos instance + speos.client.close() + return result_design_list + + +def get_design_values(osl): + """ + + Parameters + ---------- + osl: ansys.optislang.core + Ansys optiSLang instance + + Returns + ------- + list of dict: + A list of dictionaries containing the all design information for each design. + """ + project_tree = osl.osl_server.get_full_project_tree() + project_actors = project_tree["projects"][0]["system"] + algorithmsystem_uid = project_actors["nodes"][0]["uid"] + actor_info = osl.osl_server.get_actor_status_info( + algorithmsystem_uid, 0, include_design_values=True + ) + + return actor_info + + +def get_design_quality_values(osl): + """ + This function calculates the probability of failure for each response. + osl: ansys.optislang.core + Ansys optiSLang instance + + Returns + ------- + real + The probability of failure for the response as well as the number of feasible designs. + """ + list_design_information = get_design_values(osl) + + rms_contrast_count_failed = 0 + average_count_failed = 0 + rules_specification_count_failed = 0 + rules_count_failed = 0 + feasible_designs = 0 + + for design_status in list_design_information.get("design_status"): + if design_status.get("feasible"): + feasible_designs += 1 + + response_names = list_design_information.get("designs").get("response_names") + designs = list_design_information.get("designs").get("values") + + for design_values in designs: + response_values = design_values.get("response_values") + responses = {name: value for name, value in zip(response_names, response_values)} + + if responses.get("RMS_contrast") > 0.2: + rms_contrast_count_failed += 1 + if responses.get("Average") < 160000.0: + average_count_failed += 1 + if responses.get("Number_of_rules_limited_passed") > 2.0: + rules_specification_count_failed += 1 + if responses.get("Number_of_rules_failed") > 0.0: + rules_count_failed += 1 + + return ( + rms_contrast_count_failed / len(designs) * 100, + average_count_failed / len(designs) * 100, + rules_specification_count_failed / len(designs) * 100, + rules_count_failed / len(designs) * 100, + feasible_designs, + ) + + +############################################################################### +# Main script execution +# --------------------- + +# optiSLang Project creation and workflow setup +osl_executable = get_executable(252) +my_osl = Optislang( + executable=osl_executable, + ini_timeout=60, + # loglevel="DEBUG" +) +print(f"Using optiSLang version {my_osl.osl_version_string}") + +# Create workflow +parametric_system, proxy_node = create_workflow(my_osl, VARIATION_ANALYSIS_BOUNDARIES) + +# Create criteria definition +criteria_definition(parametric_system, VARIATION_ANALYSIS_BOUNDARIES) + +# Save project +temp_dir = pathlib.Path(os.getenv("TEMP")) +project_name = "_proxy_solver_workflow.opf" +# my_osl.application.save_as(temp_dir / project_name) # uncomment to save the project + +# optiSLang project execution +my_osl.application.project.start(wait_for_finished=False) +print("Variation Analysis: started.") + +# Run Robustness analysis and loop until get_status() +# returns "Processing done" for the root system +while not my_osl.project.root_system.get_status() == "Processing done": + design_list = proxy_node.get_designs() + if len(design_list): + responses_dict = calculate(design_list) + proxy_node.set_designs(responses_dict) + time.sleep(1) + +# Run MOP node +my_osl.application.project.start(wait_for_finished=True) +print("Variation Analysis: Done!") + +# Get quality values +( + rms_contrast_fail_prob, + average_fail_prob, + specification_rules_fail_prob, + national_rules_fail_prob, + num_feasible_designs, +) = get_design_quality_values(my_osl) + +print(f'{"*" * 25} SUMMARY {"*" * 25}') +print(f"Probability of failure for RMS contrast: {round(rms_contrast_fail_prob, 1)} %") +print(f"Probability of failure for Average: {round(average_fail_prob, 1)} %") +print( + f"Probability of failure for fulfillment national rules: {round(national_rules_fail_prob, 1)} %" +) +print( + "Probability of failure for fulfillment specification rules: " + f"{round(specification_rules_fail_prob, 1)} %\n" +) +print( + f"{num_feasible_designs} out of {VARIATION_ANALYSIS_BOUNDARIES['maximum_number_simulations']} " + "designs are feasible." +) +print("*" * 50) + +# Close optiSLang +my_osl.dispose() +print("OSL Project finished.")