Skip to content

Commit

Permalink
Q2D cable example (#4000)
Browse files Browse the repository at this point in the history
* New Q2D example following the wish of a customer

* Apply suggestions from code review

Co-authored-by: Kathy Pippert <[email protected]>

* fix syntax error + clean code

* fix syntax error

* rename designs + create_region

* fix names

* fix names

* Apply suggestions from code review

Co-authored-by: Kathy Pippert <[email protected]>

* commit suggestion

* improve example

---------

Co-authored-by: gmalinve <[email protected]>
Co-authored-by: Kathy Pippert <[email protected]>
Co-authored-by: Giulia Malinverno <[email protected]>
Co-authored-by: Samuel Lopez <[email protected]>
  • Loading branch information
5 people authored Jan 15, 2024
1 parent 63f16b6 commit ecda625
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 3 deletions.
2 changes: 1 addition & 1 deletion _unittest/test_34_TwinBuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def test_19_add_dynamic_link(self, add_app):
"invalid",
model_depth="100mm",
)
with pytest.raises(ValueError):
with pytest.raises(TypeError):
assert tb.add_q3d_dynamic_component(
"Q2D_ArmouredCableExample",
"2D_Extractor_Cable",
Expand Down
259 changes: 259 additions & 0 deletions examples/05-Q3D/Q2D_Armoured_Cable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
"""
Q2D: Cable parameter identification
---------------------------------------------------
This example shows how you can use PyAEDT to perform these tasks:
- Create a Q2D design using the Modeler primitives and importing part of the geometry.
- Set up the entire simulation.
- Link the solution to a Simplorer design.
For cable information, see `4 Core Armoured Power Cable <https://www.luxingcable.com/low-voltage-cables/4-core-armoured-power-cable.html>`_
"""
#################################################################################
# Perform required imports
# ~~~~~~~~~~~~~~~~~~~~~~~~

import pyaedt
import math

#################################################################################
# Initialize core strand dimensions and positions
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Initialize cable sizing - radii in mm.

c_strand_radius = 2.575
cable_n_cores = 4
core_n_strands = 6
core_xlpe_ins_thickness = 0.5
core_xy_coord = math.ceil(3 * c_strand_radius + 2 * core_xlpe_ins_thickness)

#################################################################################
# Initialize filling and sheath dimensions
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Initialize radii of further structures incrementally adding thicknesses.

filling_radius = 1.4142 * (core_xy_coord + 3 * c_strand_radius + core_xlpe_ins_thickness + 0.5)
inner_sheath_radius = filling_radius + 0.75
armour_thickness = 3
armour_radius = inner_sheath_radius + armour_thickness
outer_sheath_radius = armour_radius + 2

#################################################################################
# Initialize armature strand dimensions
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Initialize radii.

armour_centre_pos = inner_sheath_radius + armour_thickness / 2.0
arm_strand_rad = armour_thickness / 2.0 - 0.2
n_arm_strands = 30

#################################################################################
# Initialize dictionaries
# ~~~~~~~~~~~~~~~~~~~~~~~
# Initialize dictionaries that contain all the definitions for the design
# variables and output variables.

core_params = {
"n_cores": str(cable_n_cores),
"n_strands_core": str(core_n_strands),
"c_strand_radius": str(c_strand_radius) + 'mm',
"c_strand_xy_coord": str(core_xy_coord) + 'mm'
}
outer_params = {
"filling_radius": str(filling_radius) + 'mm',
"inner_sheath_radius": str(inner_sheath_radius) + 'mm',
"armour_radius": str(armour_radius) + 'mm',
"outer_sheath_radius": str(outer_sheath_radius) + 'mm'
}
armour_params = {
"armour_centre_pos": str(armour_centre_pos) + 'mm',
"arm_strand_rad": str(arm_strand_rad) + 'mm',
"n_arm_strands": str(n_arm_strands)
}

#################################################################################
# Initialize Q2D
# ~~~~~~~~~~~~~~
# Initialize Q2D, providing the version, path to the project, and the design
# name and type.

desktop_version = "2023.2"
project_name = 'Q2D_ArmouredCableExample'
q2d_design_name = '2D_Extractor_Cable'
setup_name = "MySetupAuto"
sweep_name = "sweep1"
tb_design_name = 'CableSystem'
q2d = pyaedt.Q2d(projectname=project_name, designname=q2d_design_name, specified_version=desktop_version)

##########################################################
# Define variables from dictionaries
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Define design variables from the created dictionaries.

for k, v in core_params.items():
q2d[k] = v
for k, v in outer_params.items():
q2d[k] = v
for k, v in armour_params.items():
q2d[k] = v

##########################################################
# Create object to access 2D modeler
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create the ``mod2D`` object to access the 2D modeler easily.

mod2D = q2d.modeler
mod2D.delete()
mod2D.model_units = "mm"

#################################################################################
# Initialize required material properties
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Cable insulators require the definition of specific materials since they are not included in the Sys Library.
# Plastic, PE (cross-linked, wire, and cable grade)

mat_pe_cable_grade = q2d.materials.add_material("plastic_pe_cable_grade")
mat_pe_cable_grade.conductivity = "1.40573e-16"
mat_pe_cable_grade.permittivity = "2.09762"
mat_pe_cable_grade.dielectric_loss_tangent = "0.000264575"
mat_pe_cable_grade.update()
# Plastic, PP (10% carbon fiber)
mat_pp = q2d.materials.add_material("plastic_pp_carbon_fiber")
mat_pp.conductivity = "0.0003161"
mat_pp.update()

#####################################################################################
# Create geometry for core strands, filling, and XLPE insulation
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

mod2D.create_coordinate_system(['c_strand_xy_coord', 'c_strand_xy_coord', '0mm'], name='CS_c_strand_1')
mod2D.set_working_coordinate_system('CS_c_strand_1')
c1_id = mod2D.create_circle(['0mm', '0mm', '0mm'], 'c_strand_radius', name='c_strand_1', matname='copper')
c2_id = c1_id.duplicate_along_line(vector=['0mm', '2.0*c_strand_radius', '0mm'], nclones=2)
mod2D.duplicate_around_axis(c2_id, cs_axis="Z", angle=360 / core_n_strands, nclones=6)

fill_id = mod2D.create_circle(['0mm', '0mm', '0mm'], '3*c_strand_radius', name='c_strand_fill',
matname='plastic_pp_carbon_fiber')
fill_id.color = (255, 255, 0)
xlpe_id = mod2D.create_circle(['0mm', '0mm', '0mm'], '3*c_strand_radius+' + str(core_xlpe_ins_thickness) + 'mm',
name='c_strand_xlpe',
matname='plastic_pe_cable_grade')
xlpe_id.color = (0, 128, 128)

mod2D.set_working_coordinate_system('Global')
all_obj_names = q2d.get_all_conductors_names() + q2d.get_all_dielectrics_names()
mod2D.duplicate_around_axis(all_obj_names, cs_axis="Z", angle=360 / cable_n_cores, nclones=4)

#####################################################################################
# Create geometry for filling object
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

filling_id = mod2D.create_circle(['0mm', '0mm', '0mm'], 'filling_radius', name='Filling',
matname='plastic_pp_carbon_fiber')
filling_id.color = (255, 255, 180)

#####################################################################################
# Create geometry for inner sheath object
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

inner_sheath_id = mod2D.create_circle(['0mm', '0mm', '0mm'], 'inner_sheath_radius', name='InnerSheath',
matname='PVC plastic')
inner_sheath_id.color = (0, 0, 0)

#####################################################################################
# Create geometry for armature fill
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

arm_fill_id = mod2D.create_circle(['0mm', '0mm', '0mm'], 'armour_radius', name='ArmourFilling',
matname='plastic_pp_carbon_fiber')
arm_fill_id.color = (255, 255, 255)

#####################################################################################
# Create geometry for outer sheath
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

outer_sheath_id = mod2D.create_circle(['0mm', '0mm', '0mm'], 'outer_sheath_radius', name='OuterSheath',
matname='PVC plastic')
outer_sheath_id.color = (0, 0, 0)

#####################################################################################
# Create geometry for armature steel strands
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

arm_strand_1_id = mod2D.create_circle(['0mm', 'armour_centre_pos', '0mm'], '1.1mm', name='arm_strand_1',
matname='steel_stainless')
arm_strand_1_id.color = (128, 128, 64)
arm_strand_1_id.duplicate_around_axis('Z', '360deg/n_arm_strands', nclones='n_arm_strands')
arm_strand_names = mod2D.get_objects_w_string('arm_strand')

#####################################################################################
# Create region
# ~~~~~~~~~~~~~

region = q2d.modeler.create_region([500, 500, 500, 500, 0, 0])
region.material_name = "vacuum"

##########################################################
# Assign conductors and reference ground
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

cond_names = q2d.get_all_conductors_names()
obj = [q2d.modeler.get_object_from_name(i) for i in cond_names]
[q2d.assign_single_conductor(name='C1' + str(obj.index(i) + 1), target_objects=i, conductor_type='SignalLine') for i
in obj]
obj = [q2d.modeler.get_object_from_name(i) for i in arm_strand_names]
q2d.assign_single_conductor(name="gnd", target_objects=obj, conductor_type="ReferenceGround")
mod2D.fit_all()

##########################################################
# Assign design settings
# ~~~~~~~~~~~~~~~~~~~~~~

lumped_length = "100m"
q2d_des_settings = q2d.design_settings()
q2d_des_settings['LumpedLength'] = lumped_length
q2d.change_design_settings(q2d_des_settings)

##########################################################
# Insert setup and frequency sweep
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

q2d_setup = q2d.create_setup(setupname=setup_name)
q2d_sweep = q2d_setup.add_sweep(sweepname=sweep_name)
q2d_sweep.props["RangeType"] = "LogScale"
q2d_sweep.props["RangeStart"] = "0Hz"
q2d_sweep.props["RangeEnd"] = "3MHz"
q2d_sweep.props["RangeCount"] = 10
q2d_sweep.props["RangeSamples"] = 1
q2d_sweep.update()

##########################################################
# Analyze setup
# ~~~~~~~~~~~~~

# q2d.analyze(setup_name=setup_name)

###################################################################
# Add a Simplorer/Twin Builder design and the Q3D dynamic component
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

tb = pyaedt.TwinBuilder(designname=tb_design_name)

##########################################################
# Add a Q3D dynamic component
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~

tb.add_q3d_dynamic_component(project_name,
q2d_design_name,
setup_name,
sweep_name,
model_depth=lumped_length,
coupling_matrix_name="Original")

##########################################################
# Save project and release desktop
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

tb.save_project()
tb.release_desktop(True, True)
5 changes: 3 additions & 2 deletions pyaedt/twinbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,10 @@ def add_q3d_dynamic_component(
if not setup:
raise ValueError("Invalid setup in selected design.")
else:
if [sweep for sweep in setup[0].sweeps if sweep.name == sweep_name]:
sweeps = [s for s in app.get_sweeps(setup_name) if s == sweep_name]
if sweeps: # pragma: no cover
coupling_solution_name = "{} : {}".format(setup_name, sweep_name)
else:
else: # pragma: no cover
raise ValueError("Invalid sweep name.")
if not [m for m in app.matrices if m.name == coupling_matrix_name]:
raise ValueError("Invalid matrix name.")
Expand Down

0 comments on commit ecda625

Please sign in to comment.