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.")