From 8ed1964c4b55a21e9fe094b85e3de1fd72e07c5e Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Tue, 20 Jun 2023 10:02:51 +0100 Subject: [PATCH 01/21] Initial commit --- .pre-commit-config.yaml | 52 +++--- src/ansys/motorcad/core/geometry.py | 252 +++++++++++++++++++++++++++- 2 files changed, 277 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 48235efe4..d62758abb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,29 +22,29 @@ repos: rev: 6.0.0 hooks: - id: flake8 - -- repo: https://github.com/codespell-project/codespell - rev: v2.2.2 - hooks: - - id: codespell - args: ["--ignore-words=ignore_words.txt", - "--exclude-file=exclude_lines.txt"] - -- repo: https://github.com/pycqa/pydocstyle - rev: 6.1.1 - hooks: - - id: pydocstyle - additional_dependencies: [toml] - exclude: tests/|examples/ - -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: check-merge-conflict - - id: debug-statements - -# this validates our github workflow files -- repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.21.0 - hooks: - - id: check-github-workflows \ No newline at end of file +# +#- repo: https://github.com/codespell-project/codespell +# rev: v2.2.2 +# hooks: +# - id: codespell +# args: ["--ignore-words=ignore_words.txt", +# "--exclude-file=exclude_lines.txt"] +# +#- repo: https://github.com/pycqa/pydocstyle +# rev: 6.1.1 +# hooks: +# - id: pydocstyle +# additional_dependencies: [toml] +# exclude: tests/|examples/ +# +#- repo: https://github.com/pre-commit/pre-commit-hooks +# rev: v4.4.0 +# hooks: +# - id: check-merge-conflict +# - id: debug-statements +# +## this validates our github workflow files +#- repo: https://github.com/python-jsonschema/check-jsonschema +# rev: 0.21.0 +# hooks: +# - id: check-github-workflows \ No newline at end of file diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index b9bbc578b..566c4b335 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -1,6 +1,256 @@ """Function for ``Motor-CAD geometry`` not attached to Motor-CAD instance.""" from cmath import polar, rect -from math import degrees, radians +from math import atan2, cos, degrees, pow, radians, sin, sqrt + + +class GeometryRegion: + def __init__(self): + """Create geometry region and set parameters to defaults""" + self.entities = [] + self.number_duplications = 1 + self.colour = "(0,0,0)" + self.material = "air" + + # expect other properties to be implemented here including number duplications, material etc + + def add_entity(self, entity): + """Add entity to list of region entities + + Parameters + ---------- + entity : Line/Arc class + Line/arc entity class instance + """ + self.entities.append(entity) + + def add_polyline(self, polyline): + """Add polyline to list of region entities, polyline can be made up of line/arc entities + + Parameters + ---------- + polyline : List of line/Arc class + List of line/arc entity class instances + """ + for entity in polyline: + self.add_entity(entity) + + def remove_entity(self, entity): + """Remove the entity from the region + + Parameters + ---------- + entity : Line/Arc class + Line/arc entity class instance + """ + self.entities.remove(entity) + + # method to receive region from Motor-CAD and create python object + def _from_json(self, json): + """ + + :param json: + """ + self.Entities = json.Entities + self.number_duplications = json.number_duplications + self.colour = json.colour + self.material = json.material + + # method to convert python object to send to Motor-CAD + def _to_json(self): + """ + + :return: + """ + return [self.entities] + + # def check_geometry(): + # # call Motor-CAD API + + +class Line: + def __init__(self, start, end): + """Create line entity based upon start and end coordinates + + Parameters + ---------- + start : (float, float) + (x,y) values for start coordinate. + + end : (float, float) + (x,y) values for end coordinate. + """ + self.start = start + self.end = end + + def get_coordinate_form_percentage_distance(self, x, y, percentage): + """Get the coordinate at the percentage distance along the line from the reference coord. + + Parameters + ---------- + x : float + X coordinate value for entity reference. + + y : float + y coordinate value for entity reference. + . + percentage : float + Y coordinate value. + + Returns + ------- + x : float + X coordinate value. + y : float + Y coordinate value. + """ + if (x, y) == self.end: + coordinate_1 = self.end + coordinate_2 = self.start + else: + coordinate_1 = self.start + coordinate_2 = self.end + + length = sqrt(pow(self.start[1] - self.end[1], 2) + pow(self.start[2] - self.end[2], 2)) + + t = (length * percentage) / length + x = ((1 - t) * coordinate_1[1]) + (t * coordinate_2[1]) + y = ((1 - t) * coordinate_1[2]) + (t * coordinate_2[2]) + + return x, y + + def get_coordinate_from_distance(self, x, y, distance): + """Get the coordinate at the specified distance along the line from the reference coord. + + Parameters + ---------- + x : float + X coordinate value for entity reference. + + y : float + y coordinate value for entity reference. + . + distance : float + Y coordinate value. + + Returns + ------- + x : float + X coordinate value. + y : float + Y coordinate value. + """ + + if (x, y) == self.end: + coordinate_1 = self.end + coordinate_2 = self.start + else: + coordinate_1 = self.start + coordinate_2 = self.end + + length = sqrt(pow(self.start[1] - self.end[1], 2) + pow(self.start[2] - self.end[2], 2)) + + t = distance / length + x = ((1 - t) * coordinate_1[1]) + (t * coordinate_2[1]) + y = ((1 - t) * coordinate_1[2]) + (t * coordinate_2[2]) + + return x, y + + +class Arc: + def __init__(self, start, end, centre, radius): + """Create arc entity based upon start, end, centre and radius + + Parameters + ---------- + start : (float, float) + (x,y) values for start coordinate. + + end : (float, float) + (x,y) values for end coordinate. + + centre : (float, float) + (x,y) values for centre coordinate. + + radius : float + Arc radius + """ + self.start = start + self.end = end + self.radius = radius + self.centre = centre + + def get_coordinate_from_percentage_distance(self, x, y, percentage): + """Get the coordinate at the percentage distance along the arc from the reference coord. + + Parameters + ---------- + x : float + X coordinate value for entity reference. + + y : float + y coordinate value for entity reference. + . + percentage : float + Y coordinate value. + + Returns + ------- + x : float + X coordinate value. + y : float + Y coordinate value. + """ + radius, angle_1 = xy_to_rt(self.start[1], self.start[2]) + radius, angle_2 = xy_to_rt(self.end[1], self.end[2]) + + if self.radius == 0: + arc_angle = 0 + elif ((self.radius > 0) and (angle_1 < angle_2)) or ( + (self.radius < 0) and angle_2 > angle_1 + ): + arc_angle = 360 - angle_2 + angle_1 + else: + arc_angle = angle_2 - angle_1 + + length = self.radius * arc_angle * percentage + + return self.get_coordinate_from_distance(x, y, length, 0) + + def get_coordinate_from_distance(self, x, y, distance): + """Get the coordinate at the specified distance along the arc from the reference coordinate. + + Parameters + ---------- + x : float + X coordinate value for entity reference. + + y : float + y coordinate value for entity reference. + . + distance : float + Y coordinate value. + + Returns + ------- + x : float + X coordinate value. + y : float + Y coordinate value. + """ + if (x, y) == self.end: + if self.radius >= 0: + # anticlockwise + angle = atan2(y, x) - (distance / self.radius) + else: + angle = atan2(y, x) + (distance / self.radius) + else: + if self.radius >= 0: + # anticlockwise + angle = atan2(y, x) + (distance / self.radius) + else: + angle = atan2(y, x) - (distance / self.radius) + + return self.centre[1] + self.radius * cos(angle), self.centre[2] + self.radius * sin(angle) def xy_to_rt(x, y): From 1ff21fe8cf596068bf5c4cfbec645efbaa786f98 Mon Sep 17 00:00:00 2001 From: JackDavies Date: Tue, 20 Jun 2023 10:24:41 +0100 Subject: [PATCH 02/21] add api methods --- .../core/methods/adaptive_geometry.py | 143 ++++++++++++++++++ src/ansys/motorcad/core/rpc_methods_core.py | 2 + 2 files changed, 145 insertions(+) create mode 100644 src/ansys/motorcad/core/methods/adaptive_geometry.py diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py new file mode 100644 index 000000000..a9b0f14f4 --- /dev/null +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -0,0 +1,143 @@ +"""Methods for adaptive geometry.""" + + +class _RpcMethodsAdaptiveGeometry: + def __init__(self, mc_connection): + self.connection = mc_connection + + def add_adaptive_parameter(self, name): + """ + + Parameters + ---------- + name + + Returns + ------- + + """ + + method = "AddAdaptiveParameter" + params = [name] + return self.connection.send_and_receive(method) + + def set_adaptive_parameter_value(self, name): + """ + + Parameters + ---------- + name + + Returns + ------- + + """ + method = "SetAdaptiveParameterValue" + params = [name] + return self.connection.send_and_receive(method) + + def get_adaptive_parameter_value(self, name): + """ + + Parameters + ---------- + name + + Returns + ------- + + """ + pass + + def get_region(self, name, region): + """ + + Parameters + ---------- + name + region + + Returns + ------- + + """ + pass + + def set_region(self, region): + """ + + Parameters + ---------- + region + + Returns + ------- + + """ + pass + + def get_entities_between_poly_start_end(self, region, polyline): + """ + + Parameters + ---------- + region + polyline + + Returns + ------- + + """ + pass + + def check_closed_region(self, region): + """ + + Parameters + ---------- + region + + Returns + ------- + + """ + pass + + def check_collisions(self, region): + """ + + Parameters + ---------- + region + + Returns + ------- + + """ + pass + + def add_adaptive_region(self, region): + """ + + Parameters + ---------- + region + + Returns + ------- + + """ + pass + + def save_adaptive_script(self, filepath): + """ + + Parameters + ---------- + filepath + + Returns + ------- + + """ + pass diff --git a/src/ansys/motorcad/core/rpc_methods_core.py b/src/ansys/motorcad/core/rpc_methods_core.py index ef959fdf6..c848d2102 100644 --- a/src/ansys/motorcad/core/rpc_methods_core.py +++ b/src/ansys/motorcad/core/rpc_methods_core.py @@ -3,6 +3,7 @@ Not for direct use. Inherited by _MotorCADCore/_RpcMethodsCoreOld """ +from ansys.motorcad.core.methods.adaptive_geometry import _RpcMethodsAdaptiveGeometry from ansys.motorcad.core.methods.rpc_methods_calculations import _RpcMethodsCalculations from ansys.motorcad.core.methods.rpc_methods_fea_geometry import _RpcMethodsFEAGeometry from ansys.motorcad.core.methods.rpc_methods_general import _RpcMethodsGeneral @@ -31,6 +32,7 @@ class _RpcMethodsCore( ): def __init__(self, mc_connection): self.connection = mc_connection + self.geometry = _RpcMethodsAdaptiveGeometry(mc_connection) _RpcMethodsVariables.__init__(self, self.connection) _RpcMethodsUI.__init__(self, self.connection) From d44c6312878efefcdd3e4462b5a320aeda79015f Mon Sep 17 00:00:00 2001 From: JackDavies Date: Tue, 20 Jun 2023 10:42:53 +0100 Subject: [PATCH 03/21] add api methods --- src/ansys/motorcad/core/geometry.py | 2 +- .../core/methods/adaptive_geometry.py | 90 ++++++++----------- 2 files changed, 40 insertions(+), 52 deletions(-) diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index 566c4b335..c04269e5f 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -3,7 +3,7 @@ from math import atan2, cos, degrees, pow, radians, sin, sqrt -class GeometryRegion: +class Region: def __init__(self): """Create geometry region and set parameters to defaults""" self.entities = [] diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py index a9b0f14f4..1c112ff50 100644 --- a/src/ansys/motorcad/core/methods/adaptive_geometry.py +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -1,80 +1,81 @@ """Methods for adaptive geometry.""" +from ansys.motorcad.core.geometry import Region class _RpcMethodsAdaptiveGeometry: def __init__(self, mc_connection): self.connection = mc_connection - def add_adaptive_parameter(self, name): - """ - - Parameters - ---------- - name - - Returns - ------- - - """ + def set_adaptive_parameter_value(self, name, value): + """Sets adaptive parameter. - method = "AddAdaptiveParameter" - params = [name] - return self.connection.send_and_receive(method) - - def set_adaptive_parameter_value(self, name): - """ + If parameter does not exist then create it. Parameters ---------- - name - - Returns - ------- - + name : string + name of parameter. + value : float + value of parameter. """ method = "SetAdaptiveParameterValue" params = [name] - return self.connection.send_and_receive(method) + return self.connection.send_and_receive(method, params) def get_adaptive_parameter_value(self, name): - """ + """Get adaptive parameter. Parameters ---------- - name + name : string + name of parameter. Returns ------- - + float + value of parameter. """ - pass + method = "GetAdaptiveParameterValue" + params = [name] + return self.connection.send_and_receive(method, params) - def get_region(self, name, region): - """ + def get_region(self, name): + """Get Motor-CAD geometry region. Parameters ---------- - name - region + name : string + name of region. Returns ------- + ansys.motorcad.core.geometry.Region + Motor-CAD region object. """ - pass + method = "GetRegion" + params = [name] + raw_region = self.connection.send_and_receive(method, params) + + region = Region() + region._from_json(raw_region) + + return region def set_region(self, region): - """ + """Set Motor-CAD geometry region. Parameters ---------- - region + region : ansys.motorcad.core.geometry.Region + Motor-CAD region object. + """ - Returns - ------- + raw_region = region._to_json() - """ - pass + method = "SetRegion" + params = [raw_region] + return self.connection.send_and_receive(method, params) def get_entities_between_poly_start_end(self, region, polyline): """ @@ -116,19 +117,6 @@ def check_collisions(self, region): """ pass - def add_adaptive_region(self, region): - """ - - Parameters - ---------- - region - - Returns - ------- - - """ - pass - def save_adaptive_script(self, filepath): """ From 39ed2d9e4a0ddef4bab316af7bdc3024d3554682 Mon Sep 17 00:00:00 2001 From: JackDavies Date: Tue, 20 Jun 2023 11:08:24 +0100 Subject: [PATCH 04/21] add example spec --- geom_json_example.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 geom_json_example.json diff --git a/geom_json_example.json b/geom_json_example.json new file mode 100644 index 000000000..b23355576 --- /dev/null +++ b/geom_json_example.json @@ -0,0 +1,13 @@ +{ + "name" : "wedge", + "material" : "steel", + "colour" : "red", + "area" : 10, + "centroid" : [1,2], + "region_coordinates" : [1,2], + "symmetry" : 16, + "entities" : [ + {"type" : "line", "start" : [1,2], "end" : [1,2]}, + {"type" : "arc", "start" : [1,2], "end" : [1,2], "centre" : [1,2], "radius" : 5} + ] +} From 8438032807673149e433d8c420e34e71c6a23252 Mon Sep 17 00:00:00 2001 From: JackDavies Date: Tue, 20 Jun 2023 12:04:35 +0100 Subject: [PATCH 05/21] change format of coordinates --- geom_json_example.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/geom_json_example.json b/geom_json_example.json index b23355576..0145231d0 100644 --- a/geom_json_example.json +++ b/geom_json_example.json @@ -3,11 +3,11 @@ "material" : "steel", "colour" : "red", "area" : 10, - "centroid" : [1,2], - "region_coordinates" : [1,2], + "centroid" : {"x": 1, "y": 2}, + "region_coordinates" : {"x": 1, "y": 2}, "symmetry" : 16, "entities" : [ - {"type" : "line", "start" : [1,2], "end" : [1,2]}, - {"type" : "arc", "start" : [1,2], "end" : [1,2], "centre" : [1,2], "radius" : 5} + {"type" : "line", "start" : {"x": 1, "y": 2}, "end" : {"x": 1, "y": 2}}, + {"type" : "arc", "start" : {"x": 1, "y": 2}, "end" : {"x": 1, "y": 2}, "centre" : {"x": 1, "y": 2}, "radius" : 5} ] } From 59bd938953b1323cdf0d00c93b36bb53ee371e98 Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Thu, 29 Jun 2023 11:39:51 +0100 Subject: [PATCH 06/21] Added conversion to JSON format for geometry regions --- geom_json_example.json | 2 +- src/ansys/motorcad/core/geometry.py | 77 ++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/geom_json_example.json b/geom_json_example.json index 0145231d0..2f4da4e68 100644 --- a/geom_json_example.json +++ b/geom_json_example.json @@ -1,7 +1,7 @@ { "name" : "wedge", "material" : "steel", - "colour" : "red", + "colour" : {"r": 0, "g": 0, "b": 0}, "area" : 10, "centroid" : {"x": 1, "y": 2}, "region_coordinates" : {"x": 1, "y": 2}, diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index c04269e5f..368fed79b 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -6,10 +6,14 @@ class Region: def __init__(self): """Create geometry region and set parameters to defaults""" - self.entities = [] - self.number_duplications = 1 - self.colour = "(0,0,0)" + self.name = "" self.material = "air" + self.colour = (0, 0, 0) + self.area = 0.0 + self.centroid = (0, 0) + self.region_coordinate = (0, 0) + self.duplications = 1 + self.entities = [] # expect other properties to be implemented here including number duplications, material etc @@ -50,10 +54,34 @@ def _from_json(self, json): :param json: """ - self.Entities = json.Entities - self.number_duplications = json.number_duplications - self.colour = json.colour - self.material = json.material + # self.Entities = json.Entities + self.name = json["name"] + self.material = json["material"] + + self.colour = (json["colour"]["r"], json["colour"]["g"], json["colour"]["b"]) + self.area = json["area"] + + self.centroid = (json["centroid"]["x"], json["centroid"]["y"]) + self.region_coordinate = (json["region_coordinate"]["x"], json["region_coordinate"]["y"]) + self.duplications = json["duplications"] + + for entity in json["entities"]: + if entity["type"] == "line": + self.entities.append( + Line( + (entity["start"]["x"], entity["start"]["y"]), + (entity["end"]["x"], entity["end"]["y"]), + ) + ) + elif entity["type"] == "arc": + self.entities.append( + Arc( + (entity["start"]["x"], entity["start"]["y"]), + (entity["end"]["x"], entity["end"]["y"]), + (entity["centre"]["x"], entity["centre"]["y"]), + entity["radius"], + ) + ) # method to convert python object to send to Motor-CAD def _to_json(self): @@ -61,7 +89,40 @@ def _to_json(self): :return: """ - return [self.entities] + entities = [] + + for entity in self.entities: + if type(entity) == Line: + entities.append( + { + "type": "line", + "start": {"x": entity.start[0], "y": entity.start[1]}, + "end": {"x": entity.end[0], "y": entity.end[1]}, + } + ) + elif type(entity) == Arc: + entities.append( + { + "type": "line", + "start": {"x": entity.start[0], "y": entity.start[1]}, + "end": {"x": entity.end[0], "y": entity.end[1]}, + "centre": {"x": entity.centre[0], "y": entity.centre[1]}, + "radius": entity.radius, + } + ) + + region_dict = { + "name": self.name, + "material": self.material, + "colour": {"r": self.colour[0], "g": self.colour[1], "b": self.colour[2]}, + "area": self.area, + "centroid": {"x": self.centroid[0], "y": self.centroid[1]}, + "region_coordinate": {"x": self.region_coordinate[0], "y": self.region_coordinate[1]}, + "duplications": self.duplications, + "entities": entities, + } + + return region_dict # def check_geometry(): # # call Motor-CAD API From d20a8715bffff083c4cba65f573a5e2aa90c488f Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Tue, 11 Jul 2023 14:24:42 +0100 Subject: [PATCH 07/21] Added save adaptive script functionality --- src/ansys/motorcad/core/geometry.py | 16 ++++++++-------- .../motorcad/core/methods/adaptive_geometry.py | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index 368fed79b..813973453 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -103,7 +103,7 @@ def _to_json(self): elif type(entity) == Arc: entities.append( { - "type": "line", + "type": "arc", "start": {"x": entity.start[0], "y": entity.start[1]}, "end": {"x": entity.end[0], "y": entity.end[1]}, "centre": {"x": entity.centre[0], "y": entity.centre[1]}, @@ -143,7 +143,7 @@ def __init__(self, start, end): self.start = start self.end = end - def get_coordinate_form_percentage_distance(self, x, y, percentage): + def get_coordinate_from_percentage_distance(self, x, y, percentage): """Get the coordinate at the percentage distance along the line from the reference coord. Parameters @@ -171,11 +171,11 @@ def get_coordinate_form_percentage_distance(self, x, y, percentage): coordinate_1 = self.start coordinate_2 = self.end - length = sqrt(pow(self.start[1] - self.end[1], 2) + pow(self.start[2] - self.end[2], 2)) + length = sqrt(pow(self.start[0] - self.end[0], 2) + pow(self.start[1] - self.end[1], 2)) t = (length * percentage) / length - x = ((1 - t) * coordinate_1[1]) + (t * coordinate_2[1]) - y = ((1 - t) * coordinate_1[2]) + (t * coordinate_2[2]) + x = ((1 - t) * coordinate_1[0]) + (t * coordinate_2[0]) + y = ((1 - t) * coordinate_1[1]) + (t * coordinate_2[1]) return x, y @@ -208,11 +208,11 @@ def get_coordinate_from_distance(self, x, y, distance): coordinate_1 = self.start coordinate_2 = self.end - length = sqrt(pow(self.start[1] - self.end[1], 2) + pow(self.start[2] - self.end[2], 2)) + length = sqrt(pow(self.start[0] - self.end[0], 2) + pow(self.start[1] - self.end[1], 2)) t = distance / length - x = ((1 - t) * coordinate_1[1]) + (t * coordinate_2[1]) - y = ((1 - t) * coordinate_1[2]) + (t * coordinate_2[2]) + x = ((1 - t) * coordinate_1[0]) + (t * coordinate_2[0]) + y = ((1 - t) * coordinate_1[1]) + (t * coordinate_2[1]) return x, y diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py index 1c112ff50..84475d5a0 100644 --- a/src/ansys/motorcad/core/methods/adaptive_geometry.py +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -118,14 +118,14 @@ def check_collisions(self, region): pass def save_adaptive_script(self, filepath): - """ + """Save adaptive templates script file to Motor-CAD Parameters ---------- - filepath - - Returns - ------- - + filepath : string + full file path of script """ - pass + + method = "SaveAdaptiveScript" + params = [filepath] + return self.connection.send_and_receive(method, params) From 4d4be6c2b233e0551ce9dc1e3ddff4eb43fccc6f Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Thu, 3 Aug 2023 10:49:56 +0100 Subject: [PATCH 08/21] Updated new functionality docstrings --- .pre-commit-config.yaml | 52 ++-- src/ansys/motorcad/core/geometry.py | 243 ++++++++++++------ .../core/methods/adaptive_geometry.py | 57 ++-- tests/test_geometry.py | 51 +++- 4 files changed, 278 insertions(+), 125 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d62758abb..0c5284186 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,29 +22,29 @@ repos: rev: 6.0.0 hooks: - id: flake8 -# -#- repo: https://github.com/codespell-project/codespell -# rev: v2.2.2 -# hooks: -# - id: codespell -# args: ["--ignore-words=ignore_words.txt", -# "--exclude-file=exclude_lines.txt"] -# -#- repo: https://github.com/pycqa/pydocstyle -# rev: 6.1.1 -# hooks: -# - id: pydocstyle -# additional_dependencies: [toml] -# exclude: tests/|examples/ -# -#- repo: https://github.com/pre-commit/pre-commit-hooks -# rev: v4.4.0 -# hooks: -# - id: check-merge-conflict -# - id: debug-statements -# -## this validates our github workflow files -#- repo: https://github.com/python-jsonschema/check-jsonschema -# rev: 0.21.0 -# hooks: -# - id: check-github-workflows \ No newline at end of file + +- repo: https://github.com/codespell-project/codespell + rev: v2.2.2 + hooks: + - id: codespell + args: ["--ignore-words=ignore_words.txt", + "--exclude-file=exclude_lines.txt"] + +- repo: https://github.com/pycqa/pydocstyle + rev: 6.1.1 + hooks: + - id: pydocstyle + additional_dependencies: [toml] + exclude: tests/|examples/ + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-merge-conflict + - id: debug-statements + +# this validates our github workflow files +- repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.21.0 + hooks: + - id: check-github-workflows \ No newline at end of file diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index 813973453..9ba41df0f 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -4,8 +4,10 @@ class Region: + """Python representation of Motor-CAD geometry region.""" + def __init__(self): - """Create geometry region and set parameters to defaults""" + """Create geometry region and set parameters to defaults.""" self.name = "" self.material = "air" self.colour = (0, 0, 0) @@ -18,7 +20,7 @@ def __init__(self): # expect other properties to be implemented here including number duplications, material etc def add_entity(self, entity): - """Add entity to list of region entities + """Add entity to list of region entities. Parameters ---------- @@ -27,32 +29,57 @@ def add_entity(self, entity): """ self.entities.append(entity) - def add_polyline(self, polyline): - """Add polyline to list of region entities, polyline can be made up of line/arc entities + def insert_entity(self, index, entity): + """Insert entity to list of region entities at given index. + + Parameters + ---------- + index : Integer + Index of which to insert at + entity : Line/Arc class + Line/arc entity class instance + """ + self.entities.insert(index, entity) + + def insert_polyline(self, index, polyline): + """Insert polyline at given index, polyline can be made up of line/arc entities. Parameters ---------- + index : Integer + Index of which to insert at polyline : List of line/Arc class List of line/arc entity class instances """ - for entity in polyline: - self.add_entity(entity) + for count, entity in enumerate(polyline): + self.insert_entity(index + count, entity) - def remove_entity(self, entity): - """Remove the entity from the region + def remove_entity(self, entity_remove): + """Remove the entity from the region. Parameters ---------- - entity : Line/Arc class + entity_remove : Line/Arc class Line/arc entity class instance """ - self.entities.remove(entity) + for entity in self.entities: + if (entity.start == entity_remove.start) & (entity.end == entity_remove.end): + if type(entity) == Line: + self.entities.remove(entity) + elif type(entity) == Arc: + if (entity.centre == entity_remove.centre) & ( + entity.radius == entity_remove.radius + ): + self.entities.remove(entity) # method to receive region from Motor-CAD and create python object def _from_json(self, json): - """ + """Convert class from json object. - :param json: + Parameters + ---------- + json: Json object + Represents geometry region """ # self.Entities = json.Entities self.name = json["name"] @@ -64,53 +91,17 @@ def _from_json(self, json): self.centroid = (json["centroid"]["x"], json["centroid"]["y"]) self.region_coordinate = (json["region_coordinate"]["x"], json["region_coordinate"]["y"]) self.duplications = json["duplications"] - - for entity in json["entities"]: - if entity["type"] == "line": - self.entities.append( - Line( - (entity["start"]["x"], entity["start"]["y"]), - (entity["end"]["x"], entity["end"]["y"]), - ) - ) - elif entity["type"] == "arc": - self.entities.append( - Arc( - (entity["start"]["x"], entity["start"]["y"]), - (entity["end"]["x"], entity["end"]["y"]), - (entity["centre"]["x"], entity["centre"]["y"]), - entity["radius"], - ) - ) + self.entities = _convert_entities_from_json(json["entities"]) # method to convert python object to send to Motor-CAD def _to_json(self): - """ + """Convert from Python class to Json object. - :return: + Returns + ---------- + Json object + Geometry region json representation """ - entities = [] - - for entity in self.entities: - if type(entity) == Line: - entities.append( - { - "type": "line", - "start": {"x": entity.start[0], "y": entity.start[1]}, - "end": {"x": entity.end[0], "y": entity.end[1]}, - } - ) - elif type(entity) == Arc: - entities.append( - { - "type": "arc", - "start": {"x": entity.start[0], "y": entity.start[1]}, - "end": {"x": entity.end[0], "y": entity.end[1]}, - "centre": {"x": entity.centre[0], "y": entity.centre[1]}, - "radius": entity.radius, - } - ) - region_dict = { "name": self.name, "material": self.material, @@ -119,18 +110,17 @@ def _to_json(self): "centroid": {"x": self.centroid[0], "y": self.centroid[1]}, "region_coordinate": {"x": self.region_coordinate[0], "y": self.region_coordinate[1]}, "duplications": self.duplications, - "entities": entities, + "entities": _convert_entities_to_json(self.entities), } return region_dict - # def check_geometry(): - # # call Motor-CAD API - class Line: + """Python representation of Motor-CAD line entity.""" + def __init__(self, start, end): - """Create line entity based upon start and end coordinates + """Create line entity based upon start and end coordinates. Parameters ---------- @@ -171,7 +161,7 @@ def get_coordinate_from_percentage_distance(self, x, y, percentage): coordinate_1 = self.start coordinate_2 = self.end - length = sqrt(pow(self.start[0] - self.end[0], 2) + pow(self.start[1] - self.end[1], 2)) + length = self.get_length() t = (length * percentage) / length x = ((1 - t) * coordinate_1[0]) + (t * coordinate_2[0]) @@ -200,7 +190,6 @@ def get_coordinate_from_distance(self, x, y, distance): y : float Y coordinate value. """ - if (x, y) == self.end: coordinate_1 = self.end coordinate_2 = self.start @@ -208,18 +197,28 @@ def get_coordinate_from_distance(self, x, y, distance): coordinate_1 = self.start coordinate_2 = self.end - length = sqrt(pow(self.start[0] - self.end[0], 2) + pow(self.start[1] - self.end[1], 2)) - - t = distance / length + t = distance / self.get_length() x = ((1 - t) * coordinate_1[0]) + (t * coordinate_2[0]) y = ((1 - t) * coordinate_1[1]) + (t * coordinate_2[1]) return x, y + def get_length(self): + """Get length of line. + + Returns + ------- + :Float + Length of line + """ + return sqrt(pow(self.start[0] - self.end[0], 2) + pow(self.start[1] - self.end[1], 2)) + class Arc: + """Python representation of Motor-CAD arc entity.""" + def __init__(self, start, end, centre, radius): - """Create arc entity based upon start, end, centre and radius + """Create arc entity based upon start, end, centre and radius. Parameters ---------- @@ -261,21 +260,9 @@ def get_coordinate_from_percentage_distance(self, x, y, percentage): y : float Y coordinate value. """ - radius, angle_1 = xy_to_rt(self.start[1], self.start[2]) - radius, angle_2 = xy_to_rt(self.end[1], self.end[2]) - - if self.radius == 0: - arc_angle = 0 - elif ((self.radius > 0) and (angle_1 < angle_2)) or ( - (self.radius < 0) and angle_2 > angle_1 - ): - arc_angle = 360 - angle_2 + angle_1 - else: - arc_angle = angle_2 - angle_1 - - length = self.radius * arc_angle * percentage + length = self.get_length() * percentage - return self.get_coordinate_from_distance(x, y, length, 0) + return self.get_coordinate_from_distance(x, y, length) def get_coordinate_from_distance(self, x, y, distance): """Get the coordinate at the specified distance along the arc from the reference coordinate. @@ -311,7 +298,103 @@ def get_coordinate_from_distance(self, x, y, distance): else: angle = atan2(y, x) - (distance / self.radius) - return self.centre[1] + self.radius * cos(angle), self.centre[2] + self.radius * sin(angle) + return self.centre[0] + self.radius * cos(angle), self.centre[1] + self.radius * sin(angle) + + def get_length(self): + """Get length of arc from start to end along circumference. + + Returns + ------- + :Float + Length of arc + """ + radius, angle_1 = xy_to_rt(self.start[0], self.start[1]) + radius, angle_2 = xy_to_rt(self.end[0], self.end[1]) + + if self.radius == 0: + arc_angle = 0 + elif ((self.radius > 0) and (angle_1 > angle_2)) or ( + (self.radius < 0) and angle_2 < angle_1 + ): + arc_angle = 360 - angle_2 + angle_1 + else: + arc_angle = angle_2 - angle_1 + + return self.radius * radians(arc_angle) + + +def _convert_entities_to_json(entities): + """Get entities list as a json object. + + Parameters + ---------- + entities : List of Line/Arc objects + List of Line/Arc class objects representing entities. + + Returns + ------- + :Json object + List of json entities + """ + json_entities = [] + + for entity in entities: + if type(entity) == Line: + json_entities.append( + { + "type": "line", + "start": {"x": entity.start[0], "y": entity.start[1]}, + "end": {"x": entity.end[0], "y": entity.end[1]}, + } + ) + elif type(entity) == Arc: + json_entities.append( + { + "type": "arc", + "start": {"x": entity.start[0], "y": entity.start[1]}, + "end": {"x": entity.end[0], "y": entity.end[1]}, + "centre": {"x": entity.centre[0], "y": entity.centre[1]}, + "radius": entity.radius, + } + ) + + return json_entities + + +def _convert_entities_from_json(json_array): + """Convert entities from json object to list of Arc/Line class. + + Parameters + ---------- + json_array : Json object + Array of Json objects representing lines/arcs. + + Returns + ------- + :Object List + List of Line/Arc objects + """ + entities = [] + + for entity in json_array: + if entity["type"] == "line": + entities.append( + Line( + (entity["start"]["x"], entity["start"]["y"]), + (entity["end"]["x"], entity["end"]["y"]), + ) + ) + elif entity["type"] == "arc": + entities.append( + Arc( + (entity["start"]["x"], entity["start"]["y"]), + (entity["end"]["x"], entity["end"]["y"]), + (entity["centre"]["x"], entity["centre"]["y"]), + entity["radius"], + ) + ) + + return entities def xy_to_rt(x, y): diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py index 84475d5a0..5e80c6b69 100644 --- a/src/ansys/motorcad/core/methods/adaptive_geometry.py +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -1,15 +1,27 @@ """Methods for adaptive geometry.""" -from ansys.motorcad.core.geometry import Region +from ansys.motorcad.core.geometry import Region, _convert_entities_to_json, _set_json_entities class _RpcMethodsAdaptiveGeometry: def __init__(self, mc_connection): self.connection = mc_connection - def set_adaptive_parameter_value(self, name, value): - """Sets adaptive parameter. + # def add_adaptive_parameter(self, name): + # """Adds new adaptive parameter. + # + # If parameter does not exist then create it. + # + # Parameters + # ---------- + # name : string + # name of parameter. + # """ + # method = "AddAdaptiveParameter" + # params = [name] + # return self.connection.send_and_receive(method, params) - If parameter does not exist then create it. + def set_adaptive_parameter_value(self, name, value): + """Set adaptive parameter, if parameter does not exist then add it. Parameters ---------- @@ -19,7 +31,7 @@ def set_adaptive_parameter_value(self, name, value): value of parameter. """ method = "SetAdaptiveParameterValue" - params = [name] + params = [name, value] return self.connection.send_and_receive(method, params) def get_adaptive_parameter_value(self, name): @@ -70,7 +82,6 @@ def set_region(self, region): region : ansys.motorcad.core.geometry.Region Motor-CAD region object. """ - raw_region = region._to_json() method = "SetRegion" @@ -78,54 +89,64 @@ def set_region(self, region): return self.connection.send_and_receive(method, params) def get_entities_between_poly_start_end(self, region, polyline): - """ + """Return entities from region which are between start/end of polyline. Parameters ---------- - region - polyline + region : ansys.motorcad.core.geometry.Region + Motor-CAD region object. + polyline : List of Line/Arc objects + Polyline using lines/arcs Returns + List of Line/Arc objects ------- """ - pass + raw_region = region._to_json() + raw_entities = _convert_entities_to_json(polyline) + + method = "GetEntities_Between_PolyStartEnd" + params = [raw_region, raw_entities] + + raw_output_entities, start_index = self.connection.send_and_receive(method, params) + + return _set_json_entities(raw_output_entities), start_index def check_closed_region(self, region): - """ + """Check region is closed using region detection. Parameters ---------- - region + region : ansys.motorcad.core.geometry.Region + Motor-CAD region object. - Returns ------- """ pass def check_collisions(self, region): - """ + """Check region does not collide with other geometry regions. Parameters ---------- - region + region : ansys.motorcad.core.geometry.Region + Motor-CAD region object. - Returns ------- """ pass def save_adaptive_script(self, filepath): - """Save adaptive templates script file to Motor-CAD + """Save adaptive templates script file to Motor-CAD. Parameters ---------- filepath : string full file path of script """ - method = "SaveAdaptiveScript" params = [filepath] return self.connection.send_and_receive(method, params) diff --git a/tests/test_geometry.py b/tests/test_geometry.py index 8941593a2..2e11833ed 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -1,6 +1,6 @@ import pytest -from ansys.motorcad.core import MotorCADError +from ansys.motorcad.core import MotorCADError, geometry from setup_test import setup_test_env # Get Motor-CAD exe @@ -51,3 +51,52 @@ def test_check_if_geometry_is_valid(): mc.check_if_geometry_is_valid(1) mc.set_variable("Slot_Depth", save_slot_depth) + + +def test_get_region(): + with pytest.raises(MotorCADError): + mc.geometry.get_region("Stator") + + +def test_set_region(): + region = geometry.Region() + region.name = "testing_region" + region.colour = (0, 0, 255) + region.material = "Air" + + with pytest.raises(MotorCADError): + mc.geometry.set_region(region) + + +def test_region_add_entity(): + region = geometry.Region() + region.add_entity(geometry.Line((0, 0), (1, 1))) + + assert region.entities.count() == 1 + + +def test_region_add_polyline(): + region = geometry.Region() + region.add_polyline( + [ + geometry.Line((0, 0), (1, 1)), + geometry.Line((1, 1), (1, 0)), + geometry.Line((1, 0), (0, 0)), + ] + ) + + assert region.entities.count() == 3 + + +def test_region_remove_entity(): + region = geometry.Region() + region.add_polyline( + [ + geometry.Line((0, 0), (1, 1)), + geometry.Line((1, 1), (1, 0)), + geometry.Line((1, 0), (0, 0)), + ] + ) + region.remove_entity(region.entities[1]) + + assert region.entities.count() == 2 From 88494d5e829a12f6e12df86dee622430bc065585 Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Thu, 3 Aug 2023 12:00:28 +0100 Subject: [PATCH 09/21] Refactored json entity conversion procedure names --- .../core/methods/adaptive_geometry.py | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py index 5e80c6b69..13b1484db 100644 --- a/src/ansys/motorcad/core/methods/adaptive_geometry.py +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -1,25 +1,15 @@ """Methods for adaptive geometry.""" -from ansys.motorcad.core.geometry import Region, _convert_entities_to_json, _set_json_entities +from ansys.motorcad.core.geometry import ( + Region, + _convert_entities_from_json, + _convert_entities_to_json, +) class _RpcMethodsAdaptiveGeometry: def __init__(self, mc_connection): self.connection = mc_connection - # def add_adaptive_parameter(self, name): - # """Adds new adaptive parameter. - # - # If parameter does not exist then create it. - # - # Parameters - # ---------- - # name : string - # name of parameter. - # """ - # method = "AddAdaptiveParameter" - # params = [name] - # return self.connection.send_and_receive(method, params) - def set_adaptive_parameter_value(self, name, value): """Set adaptive parameter, if parameter does not exist then add it. @@ -99,9 +89,9 @@ def get_entities_between_poly_start_end(self, region, polyline): Polyline using lines/arcs Returns - List of Line/Arc objects ------- - + :Object List + List of Line/Arc objects """ raw_region = region._to_json() raw_entities = _convert_entities_to_json(polyline) @@ -111,7 +101,7 @@ def get_entities_between_poly_start_end(self, region, polyline): raw_output_entities, start_index = self.connection.send_and_receive(method, params) - return _set_json_entities(raw_output_entities), start_index + return _convert_entities_from_json(raw_output_entities), start_index def check_closed_region(self, region): """Check region is closed using region detection. From 41058e93d738993d9dec4a7de23c596184aa1580 Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Fri, 4 Aug 2023 09:46:03 +0100 Subject: [PATCH 10/21] Added tests for new functionality --- src/ansys/motorcad/core/geometry.py | 2 +- .../core/methods/adaptive_geometry.py | 8 + tests/test_files/adaptive_templates_script.py | 13 ++ tests/test_geometry.py | 217 +++++++++++++++++- 4 files changed, 230 insertions(+), 10 deletions(-) create mode 100644 tests/test_files/adaptive_templates_script.py diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index 9ba41df0f..9b248c4d9 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -316,7 +316,7 @@ def get_length(self): elif ((self.radius > 0) and (angle_1 > angle_2)) or ( (self.radius < 0) and angle_2 < angle_1 ): - arc_angle = 360 - angle_2 + angle_1 + arc_angle = angle_2 - (angle_1 - 360) else: arc_angle = angle_2 - angle_1 diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py index 13b1484db..f4dfe728b 100644 --- a/src/ansys/motorcad/core/methods/adaptive_geometry.py +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -20,6 +20,7 @@ def set_adaptive_parameter_value(self, name, value): value : float value of parameter. """ + self.connection.ensure_version_at_least("2024.0") method = "SetAdaptiveParameterValue" params = [name, value] return self.connection.send_and_receive(method, params) @@ -37,6 +38,7 @@ def get_adaptive_parameter_value(self, name): float value of parameter. """ + self.connection.ensure_version_at_least("2024.0") method = "GetAdaptiveParameterValue" params = [name] return self.connection.send_and_receive(method, params) @@ -55,6 +57,7 @@ def get_region(self, name): Motor-CAD region object. """ + self.connection.ensure_version_at_least("2024.0") method = "GetRegion" params = [name] raw_region = self.connection.send_and_receive(method, params) @@ -72,6 +75,7 @@ def set_region(self, region): region : ansys.motorcad.core.geometry.Region Motor-CAD region object. """ + self.connection.ensure_version_at_least("2024.0") raw_region = region._to_json() method = "SetRegion" @@ -93,6 +97,7 @@ def get_entities_between_poly_start_end(self, region, polyline): :Object List List of Line/Arc objects """ + self.connection.ensure_version_at_least("2024.0") raw_region = region._to_json() raw_entities = _convert_entities_to_json(polyline) @@ -114,6 +119,7 @@ def check_closed_region(self, region): ------- """ + self.connection.ensure_version_at_least("2024.0") pass def check_collisions(self, region): @@ -127,6 +133,7 @@ def check_collisions(self, region): ------- """ + self.connection.ensure_version_at_least("2024.0") pass def save_adaptive_script(self, filepath): @@ -137,6 +144,7 @@ def save_adaptive_script(self, filepath): filepath : string full file path of script """ + self.connection.ensure_version_at_least("2024.0") method = "SaveAdaptiveScript" params = [filepath] return self.connection.send_and_receive(method, params) diff --git a/tests/test_files/adaptive_templates_script.py b/tests/test_files/adaptive_templates_script.py new file mode 100644 index 000000000..455e5e72e --- /dev/null +++ b/tests/test_files/adaptive_templates_script.py @@ -0,0 +1,13 @@ +# Need to import pymotorcad to access Motor-CAD +import ansys.motorcad.core as pymotorcad + +# Connect to Motor-CAD +mc = pymotorcad.MotorCAD(open_new_instance=False) + +# get default template region from Motor-CAD +region = mc.geometry.get_region("Stator") +# update material and colour +region.material = "M43" +region.colour = (220, 20, 60) +# set region back into Motor-CAD (updates the default stator region) +mc.geometry.set_region(region) diff --git a/tests/test_geometry.py b/tests/test_geometry.py index 2e11833ed..e90eaf991 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -1,5 +1,9 @@ +import math +from math import isclose, sqrt + import pytest +from RPC_Test_Common import get_dir_path from ansys.motorcad.core import MotorCADError, geometry from setup_test import setup_test_env @@ -53,9 +57,25 @@ def test_check_if_geometry_is_valid(): mc.set_variable("Slot_Depth", save_slot_depth) +def test_set_adaptive_parameter_value(): + with pytest.raises(MotorCADError): + mc.geometry.set_adaptive_parameter_value("test_parameter", 100) + + +def test_get_adaptive_parameter_value(): + mc.geometry.set_adaptive_parameter_value("test_parameter_1", 100) + + with pytest.raises(MotorCADError): + value = mc.geometry.get_adaptive_parameter_value("test_parameter_1") + + assert value == 100 + + def test_get_region(): with pytest.raises(MotorCADError): - mc.geometry.get_region("Stator") + region = mc.geometry.get_region("Stator") + + assert region.name == "Stator" def test_set_region(): @@ -68,35 +88,214 @@ def test_set_region(): mc.geometry.set_region(region) +def test_get_entities_between_poly_start_end(): + region = mc.geometry.get_region("Stator") + poly = [geometry.Line(region.entities[4].end, region.entities[7].end)] + + entities_to_remove, start_index = mc.geometry.get_entities_between_poly_start_end(region, poly) + + assert entities_to_remove == region.entities[4:7:1] + assert start_index == 4 + + +def test_save_adaptive_script(): + with pytest.raises(MotorCADError): + mc.geometry.save_adaptive_script( + get_dir_path() + r"\test_files\adaptive_templates_script.py" + ) + + def test_region_add_entity(): region = geometry.Region() region.add_entity(geometry.Line((0, 0), (1, 1))) - assert region.entities.count() == 1 + assert len(region.entities) == 1 + + +def test_region_insert_entity(): + region = geometry.Region() + region.add_entity(geometry.Line((0, 0), (1, 1))) + + entity = geometry.Line((-2, 2), (2, 3)) + region.insert_entity(0, entity) + + assert (len(region.entities) == 2) & (region.entities[0] == entity) -def test_region_add_polyline(): +def test_region_insert_polyline(): region = geometry.Region() - region.add_polyline( + region.insert_polyline( + 0, [ geometry.Line((0, 0), (1, 1)), geometry.Line((1, 1), (1, 0)), geometry.Line((1, 0), (0, 0)), - ] + ], ) - assert region.entities.count() == 3 + assert len(region.entities) == 3 def test_region_remove_entity(): region = geometry.Region() - region.add_polyline( + region.insert_polyline( + 0, [ geometry.Line((0, 0), (1, 1)), geometry.Line((1, 1), (1, 0)), geometry.Line((1, 0), (0, 0)), - ] + ], ) region.remove_entity(region.entities[1]) - assert region.entities.count() == 2 + assert len(region.entities) == 2 + + +def test_region_from_json(): + raw_region = { + "name": "test_region", + "material": "copper", + "colour": {"r": 240, "g": 0, "b": 0}, + "area": 5.1, + "centroid": {"x": 0.0, "y": 1.0}, + "region_coordinate": {"x": 0.0, "y": 1.1}, + "duplications": 10, + "entities": [], + } + + test_region = geometry.Region() + test_region.name = "test_region" + test_region.material = "copper" + test_region.colour = (240, 0, 0) + test_region.area = 5.1 + test_region.centroid = (0.0, 1.0) + test_region.region_coordinate = (0.0, 1.1) + test_region.duplications = 10 + test_region.entities = [] + + region = geometry.Region() + region._from_json(raw_region) + + assert region.name == test_region.name + assert region.material == test_region.material + assert region.colour == test_region.colour + assert region.area == test_region.area + assert region.centroid == test_region.centroid + assert region.region_coordinate == test_region.region_coordinate + assert region.duplications == test_region.duplications + assert region.entities == test_region.entities + + +def test_region_to_json(): + raw_region = { + "name": "test_region", + "material": "copper", + "colour": {"r": 240, "g": 0, "b": 0}, + "area": 5.1, + "centroid": {"x": 0.0, "y": 1.0}, + "region_coordinate": {"x": 0.0, "y": 1.1}, + "duplications": 10, + "entities": [], + } + + test_region = geometry.Region() + test_region.name = "test_region" + test_region.material = "copper" + test_region.colour = (240, 0, 0) + test_region.area = 5.1 + test_region.centroid = (0.0, 1.0) + test_region.region_coordinate = (0.0, 1.1) + test_region.duplications = 10 + test_region.entities = [] + + assert test_region._to_json() == raw_region + + +def test_line_get_coordinate_from_percentage_distance(): + line = geometry.Line((0, 0), (1, 1)) + + x, y = line.get_coordinate_from_percentage_distance(0, 0, 0.5) + assert (x, y) == (0.5, 0.5) + + +def test_line_get_coordinate_from_distance(): + line = geometry.Line((0, 0), (1, 1)) + + assert line.get_coordinate_from_distance(0, 0, sqrt(2) / 2) == (0.5, 0.5) + + +def test_line_get_length(): + line = geometry.Line((0, 0), (1, 1)) + + assert line.get_length() == sqrt(2) + + +def test_arc_get_coordinate_from_percentage_distance(): + arc = geometry.Arc((-1, 0), (1, 0), (0, 0), 1) + + x, y = arc.get_coordinate_from_percentage_distance(-1, 0, 0.5) + assert isclose(x, 0, abs_tol=1e-12) + assert isclose(y, -1, abs_tol=1e-12) + + +def test_arc_get_coordinate_from_distance(): + arc = geometry.Arc((-1, 0), (1, 0), (0, 0), 1) + + x, y = arc.get_coordinate_from_distance(-1, 0, math.pi / 2) + assert math.isclose(x, 0, abs_tol=1e-12) + assert math.isclose(y, -1, abs_tol=1e-12) + + +def test_arc_get_length(): + arc = geometry.Arc((-1, 0), (1, 0), (0, 0), 1) + + assert arc.get_length() == math.pi + + +def test_convert_entities_to_json(): + raw_entities = [ + {"type": "line", "start": {"x": 0.0, "y": 0.0}, "end": {"x": -1.0, "y": 0}}, + { + "type": "arc", + "start": {"x": -1.0, "y": 0.0}, + "end": {"x": 1.0, "y": 0.0}, + "centre": {"x": 0, "y": 0}, + "radius": 1.0, + }, + ] + + test_entities = [ + geometry.Line((0.0, 0.0), (-1.0, 0)), + geometry.Arc((-1.0, 0.0), (1.0, 0.0), (0.0, 0.0), 1.0), + ] + + assert geometry._convert_entities_to_json(test_entities) == raw_entities + + +def test_convert_entities_from_json(): + raw_entities = [ + {"type": "line", "start": {"x": 0.0, "y": 0.0}, "end": {"x": -1.0, "y": 0}}, + { + "type": "arc", + "start": {"x": -1.0, "y": 0.0}, + "end": {"x": 1.0, "y": 0.0}, + "centre": {"x": 0, "y": 0}, + "radius": 1.0, + }, + ] + + test_entities = [ + geometry.Line((0.0, 0.0), (-1.0, 0)), + geometry.Arc((-1.0, 0.0), (1.0, 0.0), (0.0, 0.0), 1.0), + ] + + converted_entities = geometry._convert_entities_from_json(raw_entities) + assert isinstance(converted_entities[0], type(test_entities[0])) + assert converted_entities[0].start == test_entities[0].start + assert converted_entities[0].end == test_entities[0].end + + assert isinstance(converted_entities[1], type(test_entities[1])) + assert converted_entities[1].start == test_entities[1].start + assert converted_entities[1].end == test_entities[1].end + assert converted_entities[1].centre == test_entities[1].centre + assert converted_entities[1].radius == test_entities[1].radius From 8beb015959bf57338b23382a4aaed2a94ca10f94 Mon Sep 17 00:00:00 2001 From: JackDavies Date: Fri, 4 Aug 2023 15:41:31 +0100 Subject: [PATCH 11/21] ignore version flag --- tests/setup_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/setup_test.py b/tests/setup_test.py index 35f26f00f..a27811ebc 100644 --- a/tests/setup_test.py +++ b/tests/setup_test.py @@ -30,6 +30,8 @@ def setup_test_env(): launch_new_motorcad = False + pymotorcad.rpc_client_core.DONT_CHECK_MOTORCAD_VERSION = True + try: mc except NameError: From 7bcbb4db01633d1ca364154da71976023df6508a Mon Sep 17 00:00:00 2001 From: JackDavies Date: Mon, 7 Aug 2023 10:54:18 +0100 Subject: [PATCH 12/21] Update tests and docs --- doc/source/methods/MotorCAD_object.rst | 1 + doc/source/methods/autofill_function_names.py | 2 ++ doc/source/methods/geometry_functions.rst | 22 ++++++++++++++++ doc/source/methods/index.rst | 5 ++-- src/ansys/motorcad/core/rpc_methods_core.py | 3 ++- tests/test_geometry.py | 25 ++++++++----------- 6 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 doc/source/methods/geometry_functions.rst diff --git a/doc/source/methods/MotorCAD_object.rst b/doc/source/methods/MotorCAD_object.rst index 492a1594e..032cb2f05 100644 --- a/doc/source/methods/MotorCAD_object.rst +++ b/doc/source/methods/MotorCAD_object.rst @@ -19,6 +19,7 @@ MotorCAD API _autogen_FEA Geometry _autogen_General _autogen_Geometry + _autogen_Adaptive Geometry _autogen_Graphs _autogen_Internal Scripting _autogen_Lab diff --git a/doc/source/methods/autofill_function_names.py b/doc/source/methods/autofill_function_names.py index 3291c0a91..256b9bf25 100644 --- a/doc/source/methods/autofill_function_names.py +++ b/doc/source/methods/autofill_function_names.py @@ -14,6 +14,7 @@ def generate_method_docs(): "FEA Geometry", "General", "Geometry", + "Adaptive Geometry", "Graphs", "Internal Scripting", "Lab", @@ -29,6 +30,7 @@ def generate_method_docs(): "rpc_methods_fea_geometry.py", "rpc_methods_general.py", "rpc_methods_geometry.py", + "adaptive_geometry.py", "rpc_methods_graphs.py", "rpc_methods_internal_scripting.py", "rpc_methods_lab.py", diff --git a/doc/source/methods/geometry_functions.rst b/doc/source/methods/geometry_functions.rst new file mode 100644 index 000000000..6ec6a6265 --- /dev/null +++ b/doc/source/methods/geometry_functions.rst @@ -0,0 +1,22 @@ +.. currentmodule:: ansys.motorcad.core.geometry +Adaptive Geometry +================= +Add some info here about adaptive geometry + + +Geometry Objects +---------------- +.. autosummary:: + :toctree: _autosummary_geometry_methods + + Region + Line + Arc + +Geometry Functions +------------------ +.. autosummary:: + :toctree: _autosummary_geometry_functions + + xy_to_rt + rt_to_xy \ No newline at end of file diff --git a/doc/source/methods/index.rst b/doc/source/methods/index.rst index 21a4e5855..b248cc760 100644 --- a/doc/source/methods/index.rst +++ b/doc/source/methods/index.rst @@ -4,14 +4,14 @@ API reference ============= Motor-CAD API ------------- +------------- The ``MotorCAD`` object is used by default for PyMotorCAD scripting. For descriptions of this object's single class and its many methods, see :ref:`ref_MotorCAD_object`. Motor-CAD compatibility API --------------------------- +--------------------------- The ``MotorCADCompatibility`` object is used for running old ActiveX scripts. For information on backwards compatibility, see @@ -29,3 +29,4 @@ object, its single class, and its many methods, see MotorCAD_object MotorCADCompatibility_object + geometry_functions diff --git a/src/ansys/motorcad/core/rpc_methods_core.py b/src/ansys/motorcad/core/rpc_methods_core.py index c848d2102..bea731861 100644 --- a/src/ansys/motorcad/core/rpc_methods_core.py +++ b/src/ansys/motorcad/core/rpc_methods_core.py @@ -29,10 +29,10 @@ class _RpcMethodsCore( _RpcMethodsInternalScripting, _RpcMethodsFEAGeometry, _RpcMethodsMaterials, + _RpcMethodsAdaptiveGeometry, ): def __init__(self, mc_connection): self.connection = mc_connection - self.geometry = _RpcMethodsAdaptiveGeometry(mc_connection) _RpcMethodsVariables.__init__(self, self.connection) _RpcMethodsUI.__init__(self, self.connection) @@ -45,3 +45,4 @@ def __init__(self, mc_connection): _RpcMethodsInternalScripting.__init__(self, self.connection) _RpcMethodsFEAGeometry.__init__(self, self.connection) _RpcMethodsMaterials.__init__(self, self.connection) + _RpcMethodsAdaptiveGeometry.__init__(self, self.connection) diff --git a/tests/test_geometry.py b/tests/test_geometry.py index e90eaf991..d752146b2 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -58,22 +58,19 @@ def test_check_if_geometry_is_valid(): def test_set_adaptive_parameter_value(): - with pytest.raises(MotorCADError): - mc.geometry.set_adaptive_parameter_value("test_parameter", 100) + mc.set_adaptive_parameter_value("test_parameter", 100) def test_get_adaptive_parameter_value(): - mc.geometry.set_adaptive_parameter_value("test_parameter_1", 100) + mc.set_adaptive_parameter_value("test_parameter_1", 100) - with pytest.raises(MotorCADError): - value = mc.geometry.get_adaptive_parameter_value("test_parameter_1") + value = mc.get_adaptive_parameter_value("test_parameter_1") assert value == 100 def test_get_region(): - with pytest.raises(MotorCADError): - region = mc.geometry.get_region("Stator") + region = mc.get_region("Stator") assert region.name == "Stator" @@ -84,25 +81,23 @@ def test_set_region(): region.colour = (0, 0, 255) region.material = "Air" - with pytest.raises(MotorCADError): - mc.geometry.set_region(region) + mc.set_region(region) def test_get_entities_between_poly_start_end(): - region = mc.geometry.get_region("Stator") + region = mc.get_region("Stator") poly = [geometry.Line(region.entities[4].end, region.entities[7].end)] - entities_to_remove, start_index = mc.geometry.get_entities_between_poly_start_end(region, poly) + entities_to_remove, start_index = mc.get_entities_between_poly_start_end(region, poly) assert entities_to_remove == region.entities[4:7:1] assert start_index == 4 def test_save_adaptive_script(): - with pytest.raises(MotorCADError): - mc.geometry.save_adaptive_script( - get_dir_path() + r"\test_files\adaptive_templates_script.py" - ) + mc.save_adaptive_script( + get_dir_path() + r"\test_files\adaptive_templates_script.py" + ) def test_region_add_entity(): From fba2be1ea2fc53398979f7c1e912d7d7145e9e0b Mon Sep 17 00:00:00 2001 From: JackDavies Date: Mon, 7 Aug 2023 15:43:55 +0100 Subject: [PATCH 13/21] formatting --- tests/test_geometry.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_geometry.py b/tests/test_geometry.py index d752146b2..1bf750a9e 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -58,7 +58,7 @@ def test_check_if_geometry_is_valid(): def test_set_adaptive_parameter_value(): - mc.set_adaptive_parameter_value("test_parameter", 100) + mc.set_adaptive_parameter_value("test_parameter", 100) def test_get_adaptive_parameter_value(): @@ -95,9 +95,7 @@ def test_get_entities_between_poly_start_end(): def test_save_adaptive_script(): - mc.save_adaptive_script( - get_dir_path() + r"\test_files\adaptive_templates_script.py" - ) + mc.save_adaptive_script(get_dir_path() + r"\test_files\adaptive_templates_script.py") def test_region_add_entity(): From fcf47f6d8b7e93cb1c3c8981dc5281976ab6a46a Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Tue, 8 Aug 2023 10:06:10 +0100 Subject: [PATCH 14/21] Added check_collisons API call --- .../core/methods/adaptive_geometry.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py index f4dfe728b..f4b84b2c0 100644 --- a/src/ansys/motorcad/core/methods/adaptive_geometry.py +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -122,7 +122,7 @@ def check_closed_region(self, region): self.connection.ensure_version_at_least("2024.0") pass - def check_collisions(self, region): + def check_collisions(self, region, regions_to_check): """Check region does not collide with other geometry regions. Parameters @@ -130,11 +130,26 @@ def check_collisions(self, region): region : ansys.motorcad.core.geometry.Region Motor-CAD region object. + regions_to_check : List of ansys.motorcad.core.geometry.Region + List of Motor-CAD region object + ------- """ self.connection.ensure_version_at_least("2024.0") - pass + raw_region = region._to_json() + raw_regions = [region_to_Check._to_json() for region_to_Check in regions_to_check] + + method = "Check_Collisions" + params = [raw_region, raw_regions] + + raw_collision_regions = self.connection.send_and_receive(method, params) + + collision_region = Region() + return [ + collision_region._from_json(raw_collision_region) + for raw_collision_region in raw_collision_regions + ] def save_adaptive_script(self, filepath): """Save adaptive templates script file to Motor-CAD. From 50c85f6e8cff9aadb4eed77a282ce8417a597194 Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Tue, 8 Aug 2023 10:26:29 +0100 Subject: [PATCH 15/21] Updated check_collision API --- src/ansys/motorcad/core/methods/adaptive_geometry.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py index f4b84b2c0..12b3733e5 100644 --- a/src/ansys/motorcad/core/methods/adaptive_geometry.py +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -145,11 +145,14 @@ def check_collisions(self, region, regions_to_check): raw_collision_regions = self.connection.send_and_receive(method, params) - collision_region = Region() - return [ + collision_regions = [] + + for raw_collision_region in raw_collision_regions: + collision_region = Region() collision_region._from_json(raw_collision_region) - for raw_collision_region in raw_collision_regions - ] + collision_regions.append(collision_region) + + return collision_regions def save_adaptive_script(self, filepath): """Save adaptive templates script file to Motor-CAD. From 9be7c2f2f344b1365ea23a9f435765f7fbe2d634 Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Tue, 15 Aug 2023 15:59:49 +0100 Subject: [PATCH 16/21] Review changes --- src/ansys/motorcad/core/geometry.py | 107 ++++++++++-- .../core/methods/adaptive_geometry.py | 38 +--- tests/test_geometry.py | 165 +++++++++++------- 3 files changed, 198 insertions(+), 112 deletions(-) diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index 9b248c4d9..622f91f92 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -19,12 +19,28 @@ def __init__(self): # expect other properties to be implemented here including number duplications, material etc + def __eq__(self, other): + """Override the default equals implementation for Region.""" + if ( + isinstance(other, Region) + and self.name == other.name + and self.colour == other.colour + and self.area == other.area + and self.centroid == other.centroid + and self.region_coordinate == other.region_coordinate + and self.duplications == other.duplications + and self.entities == other.entities + ): + return True + else: + return False + def add_entity(self, entity): """Add entity to list of region entities. Parameters ---------- - entity : Line/Arc class + entity : Line or Arc Line/arc entity class instance """ self.entities.append(entity) @@ -36,7 +52,7 @@ def insert_entity(self, index, entity): ---------- index : Integer Index of which to insert at - entity : Line/Arc class + entity : Line or Arc Line/arc entity class instance """ self.entities.insert(index, entity) @@ -48,7 +64,7 @@ def insert_polyline(self, index, polyline): ---------- index : Integer Index of which to insert at - polyline : List of line/Arc class + polyline : List of Line or Arc List of line/arc entity class instances """ for count, entity in enumerate(polyline): @@ -59,7 +75,7 @@ def remove_entity(self, entity_remove): Parameters ---------- - entity_remove : Line/Arc class + entity_remove : Line or Arc Line/arc entity class instance """ for entity in self.entities: @@ -115,6 +131,29 @@ def _to_json(self): return region_dict + def is_closed(self): + """Check whether region entities create a closed region. + + Returns + ---------- + Boolean + Whether region is closed + """ + if len(self.entities) > 0: + entity_first = self.entities[0] + entity_last = self.entities[-1] + + is_closed = get_entities_have_common_coordinate(entity_first, entity_last) + + for i in range(len(self.entities) - 1): + is_closed = get_entities_have_common_coordinate( + self.entities[i], self.entities[i + 1] + ) + + return is_closed + else: + return False + class Line: """Python representation of Motor-CAD line entity.""" @@ -133,6 +172,13 @@ def __init__(self, start, end): self.start = start self.end = end + def __eq__(self, other): + """Override the default equals implementation for Line.""" + if isinstance(other, Line) and self.start == other.start and self.end == other.end: + return True + else: + return False + def get_coordinate_from_percentage_distance(self, x, y, percentage): """Get the coordinate at the percentage distance along the line from the reference coord. @@ -208,13 +254,13 @@ def get_length(self): Returns ------- - :Float + float Length of line """ return sqrt(pow(self.start[0] - self.end[0], 2) + pow(self.start[1] - self.end[1], 2)) -class Arc: +class Arc(Line): """Python representation of Motor-CAD arc entity.""" def __init__(self, start, end, centre, radius): @@ -234,11 +280,23 @@ def __init__(self, start, end, centre, radius): radius : float Arc radius """ - self.start = start - self.end = end + super().__init__(start, end) self.radius = radius self.centre = centre + def __eq__(self, other): + """Override the default equals implementation for Arc.""" + if ( + isinstance(other, Arc) + and self.start == other.start + and self.end == other.end + and self.centre == other.centre + and self.radius == other.radius + ): + return True + else: + return False + def get_coordinate_from_percentage_distance(self, x, y, percentage): """Get the coordinate at the percentage distance along the arc from the reference coord. @@ -305,7 +363,7 @@ def get_length(self): Returns ------- - :Float + float Length of arc """ radius, angle_1 = xy_to_rt(self.start[0], self.start[1]) @@ -371,8 +429,8 @@ def _convert_entities_from_json(json_array): Returns ------- - :Object List - List of Line/Arc objects + :Object List of Line or Arc + List of Line or Arc objects """ entities = [] @@ -397,6 +455,33 @@ def _convert_entities_from_json(json_array): return entities +def get_entities_have_common_coordinate(entity_1, entity_2): + """Check whether region entities create a closed region. + + Parameters + ---------- + entity_1 : Line or Arc + Line or Arc object to check for common coordinate + + entity_2 : Line or Arc + Line or Arc object to check for common coordinate + + Returns + ---------- + Boolean + """ + if ( + (entity_1.end == entity_2.start) + or (entity_1.end == entity_2.end) + or (entity_1.start == entity_2.start) + or (entity_1.start == entity_2.end) + ): + # found common coordinate between first and last entities + return True + else: + return False + + def xy_to_rt(x, y): """Convert Motor-CAD Cartesian coordinates to polar coordinates in degrees. diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py index 12b3733e5..05681d839 100644 --- a/src/ansys/motorcad/core/methods/adaptive_geometry.py +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -1,9 +1,5 @@ """Methods for adaptive geometry.""" -from ansys.motorcad.core.geometry import ( - Region, - _convert_entities_from_json, - _convert_entities_to_json, -) +from ansys.motorcad.core.geometry import Region class _RpcMethodsAdaptiveGeometry: @@ -82,32 +78,6 @@ def set_region(self, region): params = [raw_region] return self.connection.send_and_receive(method, params) - def get_entities_between_poly_start_end(self, region, polyline): - """Return entities from region which are between start/end of polyline. - - Parameters - ---------- - region : ansys.motorcad.core.geometry.Region - Motor-CAD region object. - polyline : List of Line/Arc objects - Polyline using lines/arcs - - Returns - ------- - :Object List - List of Line/Arc objects - """ - self.connection.ensure_version_at_least("2024.0") - raw_region = region._to_json() - raw_entities = _convert_entities_to_json(polyline) - - method = "GetEntities_Between_PolyStartEnd" - params = [raw_region, raw_entities] - - raw_output_entities, start_index = self.connection.send_and_receive(method, params) - - return _convert_entities_from_json(raw_output_entities), start_index - def check_closed_region(self, region): """Check region is closed using region detection. @@ -115,9 +85,6 @@ def check_closed_region(self, region): ---------- region : ansys.motorcad.core.geometry.Region Motor-CAD region object. - - ------- - """ self.connection.ensure_version_at_least("2024.0") pass @@ -132,9 +99,6 @@ def check_collisions(self, region, regions_to_check): regions_to_check : List of ansys.motorcad.core.geometry.Region List of Motor-CAD region object - - ------- - """ self.connection.ensure_version_at_least("2024.0") raw_region = region._to_json() diff --git a/tests/test_geometry.py b/tests/test_geometry.py index 1bf750a9e..cdc2698ce 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -11,6 +11,19 @@ mc = setup_test_env() +def generate_constant_region(): + region = geometry.Region() + region.name = "testing_region" + region.colour = (0, 0, 255) + region.material = "Air" + + region.entities.append(geometry.Line((-1, 0), (1, 0))) + region.entities.append(geometry.Arc((1, 0), (1, 1), (0, 0), 1)) + region.entities.append(geometry.Line((1, 1), (-1, 0))) + + return region + + def test_set_get_winding_coil(): phase = 1 path = 1 @@ -58,90 +71,112 @@ def test_check_if_geometry_is_valid(): def test_set_adaptive_parameter_value(): - mc.set_adaptive_parameter_value("test_parameter", 100) + parameter_name = "test_parameter" + parameter_value = 100 + + mc.set_adaptive_parameter_value(parameter_name, parameter_value) + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Name", 0) == parameter_name + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Value", 0) == parameter_value + + parameter_value = 70 + # update existing parameter + mc.set_adaptive_parameter_value(parameter_name, parameter_value) + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Name", 0) == parameter_name + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Value", 0) == parameter_value def test_get_adaptive_parameter_value(): mc.set_adaptive_parameter_value("test_parameter_1", 100) value = mc.get_adaptive_parameter_value("test_parameter_1") - assert value == 100 + with pytest.raises(Exception) as e_info: + mc.get_adaptive_parameter_value("testing_parameter") + + assert "No adaptive parameter found with name" in str(e_info.value) + def test_get_region(): region = mc.get_region("Stator") - assert region.name == "Stator" + with pytest.raises(Exception) as e_info: + mc.get_region("Rotor_Magnet") -def test_set_region(): - region = geometry.Region() - region.name = "testing_region" - region.colour = (0, 0, 255) - region.material = "Air" + assert "No region found with name" in str(e_info.value) - mc.set_region(region) +def test_set_region(): + region = generate_constant_region() + mc.set_region(region) + returned_region = mc.get_region("testing_region") + assert returned_region == region -def test_get_entities_between_poly_start_end(): - region = mc.get_region("Stator") - poly = [geometry.Line(region.entities[4].end, region.entities[7].end)] - entities_to_remove, start_index = mc.get_entities_between_poly_start_end(region, poly) +def test_save_adaptive_script(): + filepath = get_dir_path() + r"\test_files\adaptive_templates_script.py" + mc.save_adaptive_script(filepath) - assert entities_to_remove == region.entities[4:7:1] - assert start_index == 4 + num_lines = mc.get_variable("AdaptiveTemplates_ScriptLines") + with open(filepath, "rbU") as f: + num_lines_file = sum(1 for _ in f) -def test_save_adaptive_script(): - mc.save_adaptive_script(get_dir_path() + r"\test_files\adaptive_templates_script.py") + assert num_lines == num_lines_file def test_region_add_entity(): - region = geometry.Region() - region.add_entity(geometry.Line((0, 0), (1, 1))) + # generate entity to add to region + entity = geometry.Line((0, 0), (1, 1)) - assert len(region.entities) == 1 + expected_region = generate_constant_region() + expected_region.entities.append(entity) + region = generate_constant_region() + region.add_entity(entity) -def test_region_insert_entity(): - region = geometry.Region() - region.add_entity(geometry.Line((0, 0), (1, 1))) + assert region == expected_region + +def test_region_insert_entity(): entity = geometry.Line((-2, 2), (2, 3)) - region.insert_entity(0, entity) - assert (len(region.entities) == 2) & (region.entities[0] == entity) + expected_region = generate_constant_region() + expected_region.entities.insert(1, entity) + + region = generate_constant_region() + region.insert_entity(1, entity) + + assert region == expected_region def test_region_insert_polyline(): - region = geometry.Region() - region.insert_polyline( - 0, - [ - geometry.Line((0, 0), (1, 1)), - geometry.Line((1, 1), (1, 0)), - geometry.Line((1, 0), (0, 0)), - ], - ) + polyline = [ + geometry.Line((0, 0), (1, 1)), + geometry.Arc((1, 1), (1, 0), (0, 0), 1), + geometry.Line((1, 0), (0, 0)), + ] + + expected_region = generate_constant_region() + expected_region.entities = polyline + expected_region.entities + + region = generate_constant_region() + region.insert_polyline(0, polyline) - assert len(region.entities) == 3 + assert region == expected_region def test_region_remove_entity(): - region = geometry.Region() - region.insert_polyline( - 0, - [ - geometry.Line((0, 0), (1, 1)), - geometry.Line((1, 1), (1, 0)), - geometry.Line((1, 0), (0, 0)), - ], - ) - region.remove_entity(region.entities[1]) + expected_region = generate_constant_region() + + entity = expected_region.entities[1] + expected_region.entities.remove(entity) - assert len(region.entities) == 2 + region = generate_constant_region() + region.remove_entity(entity) + + assert region == expected_region def test_region_from_json(): @@ -169,14 +204,7 @@ def test_region_from_json(): region = geometry.Region() region._from_json(raw_region) - assert region.name == test_region.name - assert region.material == test_region.material - assert region.colour == test_region.colour - assert region.area == test_region.area - assert region.centroid == test_region.centroid - assert region.region_coordinate == test_region.region_coordinate - assert region.duplications == test_region.duplications - assert region.entities == test_region.entities + assert region == test_region def test_region_to_json(): @@ -204,17 +232,23 @@ def test_region_to_json(): assert test_region._to_json() == raw_region +def test_region_is_closed(): + region = generate_constant_region() + + assert region.is_closed() + + def test_line_get_coordinate_from_percentage_distance(): - line = geometry.Line((0, 0), (1, 1)) + line = geometry.Line((0, 0), (2, 0)) x, y = line.get_coordinate_from_percentage_distance(0, 0, 0.5) - assert (x, y) == (0.5, 0.5) + assert (x, y) == (1, 0) def test_line_get_coordinate_from_distance(): - line = geometry.Line((0, 0), (1, 1)) + line = geometry.Line((0, 0), (2, 0)) - assert line.get_coordinate_from_distance(0, 0, sqrt(2) / 2) == (0.5, 0.5) + assert line.get_coordinate_from_distance(0, 0, 1) == (1, 0) def test_line_get_length(): @@ -284,11 +318,14 @@ def test_convert_entities_from_json(): converted_entities = geometry._convert_entities_from_json(raw_entities) assert isinstance(converted_entities[0], type(test_entities[0])) - assert converted_entities[0].start == test_entities[0].start - assert converted_entities[0].end == test_entities[0].end + assert converted_entities[0] == test_entities[0] assert isinstance(converted_entities[1], type(test_entities[1])) - assert converted_entities[1].start == test_entities[1].start - assert converted_entities[1].end == test_entities[1].end - assert converted_entities[1].centre == test_entities[1].centre - assert converted_entities[1].radius == test_entities[1].radius + assert converted_entities[1] == test_entities[1] + + +def test_get_entities_have_common_coordinate(): + entity_1 = geometry.Line((0, 0), (1, 1)) + entity_2 = geometry.Line((1, 1), (2, 2)) + + assert geometry.get_entities_have_common_coordinate(entity_1, entity_2) From 61a34eaaf65f81f251e2e5cd559391d93dc80174 Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Tue, 15 Aug 2023 16:24:26 +0100 Subject: [PATCH 17/21] Updated tests --- tests/test_geometry.py | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/tests/test_geometry.py b/tests/test_geometry.py index cdc2698ce..a37f27755 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -75,14 +75,19 @@ def test_set_adaptive_parameter_value(): parameter_value = 100 mc.set_adaptive_parameter_value(parameter_name, parameter_value) - assert mc.get_array_variable("AdaptiveTemplates_Parameters_Name", 0) == parameter_name - assert mc.get_array_variable("AdaptiveTemplates_Parameters_Value", 0) == parameter_value + assert mc.get_array_variable("adaptive_parameter_name", 0) == parameter_name + assert mc.get_array_variable("adaptive_parameter_value", 0) == parameter_value parameter_value = 70 # update existing parameter mc.set_adaptive_parameter_value(parameter_name, parameter_value) - assert mc.get_array_variable("AdaptiveTemplates_Parameters_Name", 0) == parameter_name - assert mc.get_array_variable("AdaptiveTemplates_Parameters_Value", 0) == parameter_value + assert mc.get_array_variable("adaptive_parameter_name", 0) == parameter_name + assert mc.get_array_variable("adaptive_parameter_value", 0) == parameter_value + + +def test_set_adaptive_parameter_value_incorrect_type(): + with pytest.raises(MotorCADError): + mc.set_adaptive_parameter_value("incorrect_type", "test_string") def test_get_adaptive_parameter_value(): @@ -91,6 +96,8 @@ def test_get_adaptive_parameter_value(): value = mc.get_adaptive_parameter_value("test_parameter_1") assert value == 100 + +def test_get_adaptive_parameter_value_does_not_exist(): with pytest.raises(Exception) as e_info: mc.get_adaptive_parameter_value("testing_parameter") @@ -98,8 +105,11 @@ def test_get_adaptive_parameter_value(): def test_get_region(): - region = mc.get_region("Stator") - assert region.name == "Stator" + expected_region = generate_constant_region() + mc.set_region(expected_region) + + region = mc.get_region(expected_region.name) + assert region == expected_region with pytest.raises(Exception) as e_info: mc.get_region("Rotor_Magnet") @@ -126,7 +136,7 @@ def test_save_adaptive_script(): assert num_lines == num_lines_file -def test_region_add_entity(): +def test_region_add_entity_line(): # generate entity to add to region entity = geometry.Line((0, 0), (1, 1)) @@ -139,6 +149,19 @@ def test_region_add_entity(): assert region == expected_region +def test_region_add_entity_arc(): + # generate entity to add to region + entity = geometry.Arc((-1, 0), (1, 0), (0, 0), 1) + + expected_region = generate_constant_region() + expected_region.entities.append(entity) + + region = generate_constant_region() + region.add_entity(entity) + + assert region == expected_region + + def test_region_insert_entity(): entity = geometry.Line((-2, 2), (2, 3)) From 009bbd99319e0bfa52c16a8e96cd5872b5b61b27 Mon Sep 17 00:00:00 2001 From: JackDavies Date: Wed, 16 Aug 2023 12:08:58 +0100 Subject: [PATCH 18/21] Minor fix to test --- tests/test_geometry.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_geometry.py b/tests/test_geometry.py index a37f27755..e338344f1 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -75,14 +75,14 @@ def test_set_adaptive_parameter_value(): parameter_value = 100 mc.set_adaptive_parameter_value(parameter_name, parameter_value) - assert mc.get_array_variable("adaptive_parameter_name", 0) == parameter_name - assert mc.get_array_variable("adaptive_parameter_value", 0) == parameter_value + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Name", 0) == parameter_name + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Value", 0) == parameter_value parameter_value = 70 # update existing parameter mc.set_adaptive_parameter_value(parameter_name, parameter_value) - assert mc.get_array_variable("adaptive_parameter_name", 0) == parameter_name - assert mc.get_array_variable("adaptive_parameter_value", 0) == parameter_value + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Name", 0) == parameter_name + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Value", 0) == parameter_value def test_set_adaptive_parameter_value_incorrect_type(): From b178f6dbde24cc8cf198cd23a8d04f500c60f8e2 Mon Sep 17 00:00:00 2001 From: matthewAnsys Date: Wed, 16 Aug 2023 14:37:04 +0100 Subject: [PATCH 19/21] Added Coordinate class --- geom_json_example.json | 13 -- src/ansys/motorcad/core/geometry.py | 243 ++++++++++++++++------------ tests/test_geometry.py | 104 +++++++----- 3 files changed, 203 insertions(+), 157 deletions(-) delete mode 100644 geom_json_example.json diff --git a/geom_json_example.json b/geom_json_example.json deleted file mode 100644 index 2f4da4e68..000000000 --- a/geom_json_example.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name" : "wedge", - "material" : "steel", - "colour" : {"r": 0, "g": 0, "b": 0}, - "area" : 10, - "centroid" : {"x": 1, "y": 2}, - "region_coordinates" : {"x": 1, "y": 2}, - "symmetry" : 16, - "entities" : [ - {"type" : "line", "start" : {"x": 1, "y": 2}, "end" : {"x": 1, "y": 2}}, - {"type" : "arc", "start" : {"x": 1, "y": 2}, "end" : {"x": 1, "y": 2}, "centre" : {"x": 1, "y": 2}, "radius" : 5} - ] -} diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index 622f91f92..c795d665d 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -12,8 +12,8 @@ def __init__(self): self.material = "air" self.colour = (0, 0, 0) self.area = 0.0 - self.centroid = (0, 0) - self.region_coordinate = (0, 0) + self.centroid = Coordinate(0, 0) + self.region_coordinate = Coordinate(0, 0) self.duplications = 1 self.entities = [] @@ -50,7 +50,7 @@ def insert_entity(self, index, entity): Parameters ---------- - index : Integer + index : int Index of which to insert at entity : Line or Arc Line/arc entity class instance @@ -62,7 +62,7 @@ def insert_polyline(self, index, polyline): Parameters ---------- - index : Integer + index : int Index of which to insert at polyline : List of Line or Arc List of line/arc entity class instances @@ -94,7 +94,7 @@ def _from_json(self, json): Parameters ---------- - json: Json object + json: dict Represents geometry region """ # self.Entities = json.Entities @@ -104,8 +104,10 @@ def _from_json(self, json): self.colour = (json["colour"]["r"], json["colour"]["g"], json["colour"]["b"]) self.area = json["area"] - self.centroid = (json["centroid"]["x"], json["centroid"]["y"]) - self.region_coordinate = (json["region_coordinate"]["x"], json["region_coordinate"]["y"]) + self.centroid = Coordinate(json["centroid"]["x"], json["centroid"]["y"]) + self.region_coordinate = Coordinate( + json["region_coordinate"]["x"], json["region_coordinate"]["y"] + ) self.duplications = json["duplications"] self.entities = _convert_entities_from_json(json["entities"]) @@ -115,16 +117,16 @@ def _to_json(self): Returns ---------- - Json object - Geometry region json representation + dict + Geometry region json representation """ region_dict = { "name": self.name, "material": self.material, "colour": {"r": self.colour[0], "g": self.colour[1], "b": self.colour[2]}, "area": self.area, - "centroid": {"x": self.centroid[0], "y": self.centroid[1]}, - "region_coordinate": {"x": self.region_coordinate[0], "y": self.region_coordinate[1]}, + "centroid": {"x": self.centroid.x, "y": self.centroid.y}, + "region_coordinate": {"x": self.region_coordinate.x, "y": self.region_coordinate.y}, "duplications": self.duplications, "entities": _convert_entities_to_json(self.entities), } @@ -136,8 +138,8 @@ def is_closed(self): Returns ---------- - Boolean - Whether region is closed + Boolean + Whether region is closed """ if len(self.entities) > 0: entity_first = self.entities[0] @@ -155,23 +157,72 @@ def is_closed(self): return False -class Line: - """Python representation of Motor-CAD line entity.""" +class Coordinate: + """Python representation of coordinate in two-dimensional space.""" - def __init__(self, start, end): + def __init__(self, x, y): """Create line entity based upon start and end coordinates. Parameters ---------- - start : (float, float) - (x,y) values for start coordinate. + x : float + X value. - end : (float, float) - (x,y) values for end coordinate. + y : float + Y value. + """ + self.x = x + self.y = y + + def __eq__(self, other): + """Override the default equals implementation for Coordinate.""" + if isinstance(other, Coordinate) and self.x == other.x and self.y == other.y: + return True + else: + return False + + +class Entity: + """Generic parent class for geometric entities based upon a start and end coordinate.""" + + def __init__(self, start, end): + """Entity initialisation function. + + Parameters + ---------- + start : Coordinate + Start coordinate. + + end : Coordinate + End coordinate. """ self.start = start self.end = end + def __eq__(self, other): + """Override the default equals implementation for Entity.""" + if isinstance(other, Entity) and self.start == other.start and self.end == other.end: + return True + else: + return False + + +class Line(Entity): + """Python representation of Motor-CAD line entity based upon start and end coordinates.""" + + def __init__(self, start, end): + """Line initialisation function. + + Parameters + ---------- + start : Coordinate + Start coordinate. + + end : Coordinate + End coordinate. + """ + super().__init__(start, end) + def __eq__(self, other): """Override the default equals implementation for Line.""" if isinstance(other, Line) and self.start == other.start and self.end == other.end: @@ -179,28 +230,23 @@ def __eq__(self, other): else: return False - def get_coordinate_from_percentage_distance(self, x, y, percentage): - """Get the coordinate at the percentage distance along the line from the reference coord. + def get_coordinate_from_percentage_distance(self, ref_coordinate, percentage): + """Get the coordinate at the percentage distance along the line from the reference. Parameters ---------- - x : float - X coordinate value for entity reference. + ref_coordinate : Coordinate + Entity reference coordinate. - y : float - y coordinate value for entity reference. - . percentage : float - Y coordinate value. + Percentage distance along Line. Returns ------- - x : float - X coordinate value. - y : float - Y coordinate value. + Coordinate + Coordinate at percentage distance along Line. """ - if (x, y) == self.end: + if ref_coordinate == self.end: coordinate_1 = self.end coordinate_2 = self.start else: @@ -210,33 +256,28 @@ def get_coordinate_from_percentage_distance(self, x, y, percentage): length = self.get_length() t = (length * percentage) / length - x = ((1 - t) * coordinate_1[0]) + (t * coordinate_2[0]) - y = ((1 - t) * coordinate_1[1]) + (t * coordinate_2[1]) + x = ((1 - t) * coordinate_1.x) + (t * coordinate_2.x) + y = ((1 - t) * coordinate_1.y) + (t * coordinate_2.y) - return x, y + return Coordinate(x, y) - def get_coordinate_from_distance(self, x, y, distance): - """Get the coordinate at the specified distance along the line from the reference coord. + def get_coordinate_from_distance(self, ref_coordinate, distance): + """Get the coordinate at the specified distance along the line from the reference. Parameters ---------- - x : float - X coordinate value for entity reference. + ref_coordinate : Coordinate + Entity reference coordinate. - y : float - y coordinate value for entity reference. - . distance : float - Y coordinate value. + Distance along Line. Returns ------- - x : float - X coordinate value. - y : float - Y coordinate value. + Coordinate + Coordinate at distance along Line. """ - if (x, y) == self.end: + if ref_coordinate == self.end: coordinate_1 = self.end coordinate_2 = self.start else: @@ -244,10 +285,10 @@ def get_coordinate_from_distance(self, x, y, distance): coordinate_2 = self.end t = distance / self.get_length() - x = ((1 - t) * coordinate_1[0]) + (t * coordinate_2[0]) - y = ((1 - t) * coordinate_1[1]) + (t * coordinate_2[1]) + x = ((1 - t) * coordinate_1.x) + (t * coordinate_2.x) + y = ((1 - t) * coordinate_1.y) + (t * coordinate_2.y) - return x, y + return Coordinate(x, y) def get_length(self): """Get length of line. @@ -257,25 +298,25 @@ def get_length(self): float Length of line """ - return sqrt(pow(self.start[0] - self.end[0], 2) + pow(self.start[1] - self.end[1], 2)) + return sqrt(pow(self.start.x - self.end.x, 2) + pow(self.start.y - self.end.y, 2)) -class Arc(Line): - """Python representation of Motor-CAD arc entity.""" +class Arc(Entity): + """Python representation of Motor-CAD arc entity based upon start, end, centre and radius.""" def __init__(self, start, end, centre, radius): - """Create arc entity based upon start, end, centre and radius. + """Arc initialisation function. Parameters ---------- - start : (float, float) - (x,y) values for start coordinate. + start : Coordinate + Start coordinate. - end : (float, float) - (x,y) values for end coordinate. + end : Coordinate + End coordinate. - centre : (float, float) - (x,y) values for centre coordinate. + centre :Coordinate + Centre coordinate. radius : float Arc radius @@ -297,66 +338,58 @@ def __eq__(self, other): else: return False - def get_coordinate_from_percentage_distance(self, x, y, percentage): + def get_coordinate_from_percentage_distance(self, ref_coordinate, percentage): """Get the coordinate at the percentage distance along the arc from the reference coord. Parameters ---------- - x : float - X coordinate value for entity reference. + ref_coordinate : Coordinate + Entity reference coordinate. - y : float - y coordinate value for entity reference. - . percentage : float - Y coordinate value. + Percentage distance along Arc. Returns ------- - x : float - X coordinate value. - y : float - Y coordinate value. + Coordinate + Coordinate at percentage distance along Arc. """ length = self.get_length() * percentage - return self.get_coordinate_from_distance(x, y, length) + return self.get_coordinate_from_distance(ref_coordinate, length) - def get_coordinate_from_distance(self, x, y, distance): + def get_coordinate_from_distance(self, ref_coordinate, distance): """Get the coordinate at the specified distance along the arc from the reference coordinate. Parameters ---------- - x : float - X coordinate value for entity reference. + ref_coordinate : Coordinate + Entity reference coordinate. - y : float - y coordinate value for entity reference. - . distance : float - Y coordinate value. + Distance along arc. Returns ------- - x : float - X coordinate value. - y : float - Y coordinate value. + Coordinate + Coordinate at distance along Arc. """ - if (x, y) == self.end: + if ref_coordinate == self.end: if self.radius >= 0: # anticlockwise - angle = atan2(y, x) - (distance / self.radius) + angle = atan2(ref_coordinate.y, ref_coordinate.x) - (distance / self.radius) else: - angle = atan2(y, x) + (distance / self.radius) + angle = atan2(ref_coordinate.y, ref_coordinate.x) + (distance / self.radius) else: if self.radius >= 0: # anticlockwise - angle = atan2(y, x) + (distance / self.radius) + angle = atan2(ref_coordinate.y, ref_coordinate.x) + (distance / self.radius) else: - angle = atan2(y, x) - (distance / self.radius) + angle = atan2(ref_coordinate.y, ref_coordinate.x) - (distance / self.radius) - return self.centre[0] + self.radius * cos(angle), self.centre[1] + self.radius * sin(angle) + return Coordinate( + self.centre.x + self.radius * cos(angle), self.centre.y + self.radius * sin(angle) + ) def get_length(self): """Get length of arc from start to end along circumference. @@ -366,8 +399,8 @@ def get_length(self): float Length of arc """ - radius, angle_1 = xy_to_rt(self.start[0], self.start[1]) - radius, angle_2 = xy_to_rt(self.end[0], self.end[1]) + radius, angle_1 = xy_to_rt(self.start.x, self.start.y) + radius, angle_2 = xy_to_rt(self.end.x, self.end.y) if self.radius == 0: arc_angle = 0 @@ -397,21 +430,21 @@ def _convert_entities_to_json(entities): json_entities = [] for entity in entities: - if type(entity) == Line: + if isinstance(entity, Line): json_entities.append( { "type": "line", - "start": {"x": entity.start[0], "y": entity.start[1]}, - "end": {"x": entity.end[0], "y": entity.end[1]}, + "start": {"x": entity.start.x, "y": entity.start.y}, + "end": {"x": entity.end.x, "y": entity.end.y}, } ) - elif type(entity) == Arc: + elif isinstance(entity, Arc): json_entities.append( { "type": "arc", - "start": {"x": entity.start[0], "y": entity.start[1]}, - "end": {"x": entity.end[0], "y": entity.end[1]}, - "centre": {"x": entity.centre[0], "y": entity.centre[1]}, + "start": {"x": entity.start.x, "y": entity.start.y}, + "end": {"x": entity.end.x, "y": entity.end.y}, + "centre": {"x": entity.centre.x, "y": entity.centre.y}, "radius": entity.radius, } ) @@ -438,16 +471,16 @@ def _convert_entities_from_json(json_array): if entity["type"] == "line": entities.append( Line( - (entity["start"]["x"], entity["start"]["y"]), - (entity["end"]["x"], entity["end"]["y"]), + Coordinate(entity["start"]["x"], entity["start"]["y"]), + Coordinate(entity["end"]["x"], entity["end"]["y"]), ) ) elif entity["type"] == "arc": entities.append( Arc( - (entity["start"]["x"], entity["start"]["y"]), - (entity["end"]["x"], entity["end"]["y"]), - (entity["centre"]["x"], entity["centre"]["y"]), + Coordinate(entity["start"]["x"], entity["start"]["y"]), + Coordinate(entity["end"]["x"], entity["end"]["y"]), + Coordinate(entity["centre"]["x"], entity["centre"]["y"]), entity["radius"], ) ) diff --git a/tests/test_geometry.py b/tests/test_geometry.py index a37f27755..8009e4d62 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -17,9 +17,13 @@ def generate_constant_region(): region.colour = (0, 0, 255) region.material = "Air" - region.entities.append(geometry.Line((-1, 0), (1, 0))) - region.entities.append(geometry.Arc((1, 0), (1, 1), (0, 0), 1)) - region.entities.append(geometry.Line((1, 1), (-1, 0))) + region.entities.append(geometry.Line(geometry.Coordinate(-1, 0), geometry.Coordinate(1, 0))) + region.entities.append( + geometry.Arc( + geometry.Coordinate(1, 0), geometry.Coordinate(1, 1), geometry.Coordinate(0, 0), 1 + ) + ) + region.entities.append(geometry.Line(geometry.Coordinate(1, 1), geometry.Coordinate(-1, 0))) return region @@ -75,14 +79,14 @@ def test_set_adaptive_parameter_value(): parameter_value = 100 mc.set_adaptive_parameter_value(parameter_name, parameter_value) - assert mc.get_array_variable("adaptive_parameter_name", 0) == parameter_name - assert mc.get_array_variable("adaptive_parameter_value", 0) == parameter_value + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Name", 0) == parameter_name + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Value", 0) == parameter_value parameter_value = 70 # update existing parameter mc.set_adaptive_parameter_value(parameter_name, parameter_value) - assert mc.get_array_variable("adaptive_parameter_name", 0) == parameter_name - assert mc.get_array_variable("adaptive_parameter_value", 0) == parameter_value + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Name", 0) == parameter_name + assert mc.get_array_variable("AdaptiveTemplates_Parameters_Value", 0) == parameter_value def test_set_adaptive_parameter_value_incorrect_type(): @@ -114,7 +118,7 @@ def test_get_region(): with pytest.raises(Exception) as e_info: mc.get_region("Rotor_Magnet") - assert "No region found with name" in str(e_info.value) + assert "Failed to find region with name" in str(e_info.value) def test_set_region(): @@ -138,7 +142,7 @@ def test_save_adaptive_script(): def test_region_add_entity_line(): # generate entity to add to region - entity = geometry.Line((0, 0), (1, 1)) + entity = geometry.Line(geometry.Coordinate(0, 0), geometry.Coordinate(1, 1)) expected_region = generate_constant_region() expected_region.entities.append(entity) @@ -151,7 +155,9 @@ def test_region_add_entity_line(): def test_region_add_entity_arc(): # generate entity to add to region - entity = geometry.Arc((-1, 0), (1, 0), (0, 0), 1) + entity = geometry.Arc( + geometry.Coordinate(-1, 0), geometry.Coordinate(1, 0), geometry.Coordinate(0, 0), 1 + ) expected_region = generate_constant_region() expected_region.entities.append(entity) @@ -163,7 +169,7 @@ def test_region_add_entity_arc(): def test_region_insert_entity(): - entity = geometry.Line((-2, 2), (2, 3)) + entity = geometry.Line(geometry.Coordinate(-2, 2), geometry.Coordinate(2, 3)) expected_region = generate_constant_region() expected_region.entities.insert(1, entity) @@ -176,9 +182,11 @@ def test_region_insert_entity(): def test_region_insert_polyline(): polyline = [ - geometry.Line((0, 0), (1, 1)), - geometry.Arc((1, 1), (1, 0), (0, 0), 1), - geometry.Line((1, 0), (0, 0)), + geometry.Line(geometry.Coordinate(0, 0), geometry.Coordinate(1, 1)), + geometry.Arc( + geometry.Coordinate(1, 1), geometry.Coordinate(1, 0), geometry.Coordinate(0, 0), 1 + ), + geometry.Line(geometry.Coordinate(1, 0), geometry.Coordinate(0, 0)), ] expected_region = generate_constant_region() @@ -219,8 +227,8 @@ def test_region_from_json(): test_region.material = "copper" test_region.colour = (240, 0, 0) test_region.area = 5.1 - test_region.centroid = (0.0, 1.0) - test_region.region_coordinate = (0.0, 1.1) + test_region.centroid = geometry.Coordinate(0.0, 1.0) + test_region.region_coordinate = geometry.Coordinate(0.0, 1.1) test_region.duplications = 10 test_region.entities = [] @@ -247,8 +255,8 @@ def test_region_to_json(): test_region.material = "copper" test_region.colour = (240, 0, 0) test_region.area = 5.1 - test_region.centroid = (0.0, 1.0) - test_region.region_coordinate = (0.0, 1.1) + test_region.centroid = geometry.Coordinate(0.0, 1.0) + test_region.region_coordinate = geometry.Coordinate(0.0, 1.1) test_region.duplications = 10 test_region.entities = [] @@ -262,42 +270,50 @@ def test_region_is_closed(): def test_line_get_coordinate_from_percentage_distance(): - line = geometry.Line((0, 0), (2, 0)) + line = geometry.Line(geometry.Coordinate(0, 0), geometry.Coordinate(2, 0)) - x, y = line.get_coordinate_from_percentage_distance(0, 0, 0.5) - assert (x, y) == (1, 0) + coord = line.get_coordinate_from_percentage_distance(geometry.Coordinate(0, 0), 0.5) + assert coord == geometry.Coordinate(1, 0) def test_line_get_coordinate_from_distance(): - line = geometry.Line((0, 0), (2, 0)) + line = geometry.Line(geometry.Coordinate(0, 0), geometry.Coordinate(2, 0)) - assert line.get_coordinate_from_distance(0, 0, 1) == (1, 0) + assert line.get_coordinate_from_distance(geometry.Coordinate(0, 0), 1) == geometry.Coordinate( + 1, 0 + ) def test_line_get_length(): - line = geometry.Line((0, 0), (1, 1)) + line = geometry.Line(geometry.Coordinate(0, 0), geometry.Coordinate(1, 1)) assert line.get_length() == sqrt(2) def test_arc_get_coordinate_from_percentage_distance(): - arc = geometry.Arc((-1, 0), (1, 0), (0, 0), 1) + arc = geometry.Arc( + geometry.Coordinate(-1, 0), geometry.Coordinate(1, 0), geometry.Coordinate(0, 0), 1 + ) - x, y = arc.get_coordinate_from_percentage_distance(-1, 0, 0.5) - assert isclose(x, 0, abs_tol=1e-12) - assert isclose(y, -1, abs_tol=1e-12) + coord = arc.get_coordinate_from_percentage_distance(geometry.Coordinate(-1, 0), 0.5) + assert isclose(coord.x, 0, abs_tol=1e-12) + assert isclose(coord.y, -1, abs_tol=1e-12) def test_arc_get_coordinate_from_distance(): - arc = geometry.Arc((-1, 0), (1, 0), (0, 0), 1) + arc = geometry.Arc( + geometry.Coordinate(-1, 0), geometry.Coordinate(1, 0), geometry.Coordinate(0, 0), 1 + ) - x, y = arc.get_coordinate_from_distance(-1, 0, math.pi / 2) - assert math.isclose(x, 0, abs_tol=1e-12) - assert math.isclose(y, -1, abs_tol=1e-12) + coord = arc.get_coordinate_from_distance(geometry.Coordinate(-1, 0), math.pi / 2) + assert math.isclose(coord.x, 0, abs_tol=1e-12) + assert math.isclose(coord.y, -1, abs_tol=1e-12) def test_arc_get_length(): - arc = geometry.Arc((-1, 0), (1, 0), (0, 0), 1) + arc = geometry.Arc( + geometry.Coordinate(-1, 0), geometry.Coordinate(1, 0), geometry.Coordinate(0, 0), 1 + ) assert arc.get_length() == math.pi @@ -315,8 +331,13 @@ def test_convert_entities_to_json(): ] test_entities = [ - geometry.Line((0.0, 0.0), (-1.0, 0)), - geometry.Arc((-1.0, 0.0), (1.0, 0.0), (0.0, 0.0), 1.0), + geometry.Line(geometry.Coordinate(0.0, 0.0), geometry.Coordinate(-1.0, 0)), + geometry.Arc( + geometry.Coordinate(-1.0, 0.0), + geometry.Coordinate(1.0, 0.0), + geometry.Coordinate(0.0, 0.0), + 1.0, + ), ] assert geometry._convert_entities_to_json(test_entities) == raw_entities @@ -335,8 +356,13 @@ def test_convert_entities_from_json(): ] test_entities = [ - geometry.Line((0.0, 0.0), (-1.0, 0)), - geometry.Arc((-1.0, 0.0), (1.0, 0.0), (0.0, 0.0), 1.0), + geometry.Line(geometry.Coordinate(0.0, 0.0), geometry.Coordinate(-1.0, 0)), + geometry.Arc( + geometry.Coordinate(-1.0, 0.0), + geometry.Coordinate(1.0, 0.0), + geometry.Coordinate(0.0, 0.0), + 1.0, + ), ] converted_entities = geometry._convert_entities_from_json(raw_entities) @@ -348,7 +374,7 @@ def test_convert_entities_from_json(): def test_get_entities_have_common_coordinate(): - entity_1 = geometry.Line((0, 0), (1, 1)) - entity_2 = geometry.Line((1, 1), (2, 2)) + entity_1 = geometry.Line(geometry.Coordinate(0, 0), geometry.Coordinate(1, 1)) + entity_2 = geometry.Line(geometry.Coordinate(1, 1), geometry.Coordinate(2, 2)) assert geometry.get_entities_have_common_coordinate(entity_1, entity_2) From c1c669b7e35e29be7b8130b4d17454dc4b71043b Mon Sep 17 00:00:00 2001 From: JackDavies Date: Wed, 16 Aug 2023 15:40:28 +0100 Subject: [PATCH 20/21] tidy up doc strings --- src/ansys/motorcad/core/geometry.py | 102 +++++++++--------- .../core/methods/adaptive_geometry.py | 4 +- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/ansys/motorcad/core/geometry.py b/src/ansys/motorcad/core/geometry.py index c795d665d..6fd0df422 100644 --- a/src/ansys/motorcad/core/geometry.py +++ b/src/ansys/motorcad/core/geometry.py @@ -64,8 +64,8 @@ def insert_polyline(self, index, polyline): ---------- index : int Index of which to insert at - polyline : List of Line or Arc - List of line/arc entity class instances + polyline : list of Line or list of Arc + list of Line or list of Arc """ for count, entity in enumerate(polyline): self.insert_entity(index + count, entity) @@ -158,19 +158,19 @@ def is_closed(self): class Coordinate: - """Python representation of coordinate in two-dimensional space.""" + """Python representation of coordinate in two-dimensional space. - def __init__(self, x, y): - """Create line entity based upon start and end coordinates. + Parameters + ---------- + x : float + X value. - Parameters - ---------- - x : float - X value. + y : float + Y value. + """ - y : float - Y value. - """ + def __init__(self, x, y): + """Initialise Coordinate.""" self.x = x self.y = y @@ -183,19 +183,19 @@ def __eq__(self, other): class Entity: - """Generic parent class for geometric entities based upon a start and end coordinate.""" + """Generic parent class for geometric entities based upon a start and end coordinate. - def __init__(self, start, end): - """Entity initialisation function. + Parameters + ---------- + start : Coordinate + Start coordinate. - Parameters - ---------- - start : Coordinate - Start coordinate. + end : Coordinate + End coordinate. + """ - end : Coordinate - End coordinate. - """ + def __init__(self, start, end): + """Initialise Entity.""" self.start = start self.end = end @@ -208,19 +208,19 @@ def __eq__(self, other): class Line(Entity): - """Python representation of Motor-CAD line entity based upon start and end coordinates.""" + """Python representation of Motor-CAD line entity based upon start and end coordinates. - def __init__(self, start, end): - """Line initialisation function. + Parameters + ---------- + start : Coordinate + Start coordinate. - Parameters - ---------- - start : Coordinate - Start coordinate. + end : Coordinate + End coordinate. + """ - end : Coordinate - End coordinate. - """ + def __init__(self, start, end): + """Initialise Line.""" super().__init__(start, end) def __eq__(self, other): @@ -302,25 +302,25 @@ def get_length(self): class Arc(Entity): - """Python representation of Motor-CAD arc entity based upon start, end, centre and radius.""" + """Python representation of Motor-CAD arc entity based upon start, end, centre and radius. - def __init__(self, start, end, centre, radius): - """Arc initialisation function. + Parameters + ---------- + start : Coordinate + Start coordinate. - Parameters - ---------- - start : Coordinate - Start coordinate. + end : Coordinate + End coordinate. - end : Coordinate - End coordinate. + centre :Coordinate + Centre coordinate. - centre :Coordinate - Centre coordinate. + radius : float + Arc radius + """ - radius : float - Arc radius - """ + def __init__(self, start, end, centre, radius): + """Initialise Arc.""" super().__init__(start, end) self.radius = radius self.centre = centre @@ -419,13 +419,13 @@ def _convert_entities_to_json(entities): Parameters ---------- - entities : List of Line/Arc objects + entities : list of Line or list of Arc List of Line/Arc class objects representing entities. Returns ------- - :Json object - List of json entities + dict + entities in json format """ json_entities = [] @@ -462,8 +462,8 @@ def _convert_entities_from_json(json_array): Returns ------- - :Object List of Line or Arc - List of Line or Arc objects + list of Line or list of Arc + list of Line and Arc objects """ entities = [] diff --git a/src/ansys/motorcad/core/methods/adaptive_geometry.py b/src/ansys/motorcad/core/methods/adaptive_geometry.py index 05681d839..db8437985 100644 --- a/src/ansys/motorcad/core/methods/adaptive_geometry.py +++ b/src/ansys/motorcad/core/methods/adaptive_geometry.py @@ -97,8 +97,8 @@ def check_collisions(self, region, regions_to_check): region : ansys.motorcad.core.geometry.Region Motor-CAD region object. - regions_to_check : List of ansys.motorcad.core.geometry.Region - List of Motor-CAD region object + regions_to_check : list of ansys.motorcad.core.geometry.Region + list of Motor-CAD region object """ self.connection.ensure_version_at_least("2024.0") raw_region = region._to_json() From 330e816b1c19cd66eaaad51501c40b06787445ca Mon Sep 17 00:00:00 2001 From: JackDavies Date: Wed, 16 Aug 2023 16:53:36 +0100 Subject: [PATCH 21/21] error message changed - make assert less strict --- tests/test_geometry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_geometry.py b/tests/test_geometry.py index 8009e4d62..109ac10f8 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -118,7 +118,7 @@ def test_get_region(): with pytest.raises(Exception) as e_info: mc.get_region("Rotor_Magnet") - assert "Failed to find region with name" in str(e_info.value) + assert ("region" in str(e_info.value)) and ("name" in str(e_info.value)) def test_set_region():