LubomirJagos / FreeCAD-OpenEMS-Export

Simple GUI plugin for FreeCAD to export current model for EM simulation in OpenEMS
GNU General Public License v3.0
61 stars 5 forks source link

Bugfixes and overhaul #7

Closed LubomirJagos closed 1 year ago

LubomirJagos commented 1 year ago

Correcting code supposed by MisterHW and adding some new features.

fix nano and pico units add appropriate scaling to getUnitsAsNumber() "Fixed Distance" output remove getUnitsAsNumber() re-implementation in class LumpedPartSettingsItem add .m code to ensure working directory matches .m path to prevent AppCSXCad/OpenEMS crashes

MisterHW commented 1 year ago

A note on units: I hope 8686a64 and 74f941e have managed to resolve some of the confusion and uncertainty about the different types of units FreeCAD-OpenEMS-Export comes in contact with.

  1. The physical constants used by OpenEMS assume the MKS system of units. This doesn't mean all lengths have to be given in meters, but the units have to be considered when using those constants: https://github.com/thliebig/openEMS/blob/master/matlab/physical_constants.m

  2. FreeCAD uses mm internally, which consequently also applies to STL export. While this is common knowledge, it took me a bit to find any explicit way to obtain the value, and I ultimately ended up hard-coding it anyway (though with some commentary). Therefore, fc_unit = 0.001; is introduced to the .m script.

  3. In OpenEMS, the length unit based on which ports, boxes and grid lines are specified are user-defined. Popular choices are unit = 1e-3; and unit = 1e-6;. Attention has to be paid to how the length unit is being set and used though. In essence, when DefineRectGrid(CSX, deltaUnit, mesh) called, CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit = deltaUnit; is set, which is copied elsewhere, e.g. in port.drawingunit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit;. Inferred best practice: call DefineRectGrid early to pass unit and use the same unit in subsequent calls.

  4. Item values entered in the UI are converted based on their settingsItem.unit over unit.

  5. Other geometry values in the UI are given in mm and the the fields are labeled accordingly to avoid confusion.

A combo box has been added to the Simulation Params tab to make this geometry unit selectable (default: mm). By having both the geometry unit unit and the FreeCAD unit fc_unit,


References

https://github.com/FreeCAD/FreeCAD/blob/8aff4a750351b609dfab12dcb550651777957d1b/src/Base/UnitsSchema.h#L34

enum class UnitSystem {
    SI1 = 0 , /** internal (mm,kg,s) SI system (http://en.wikipedia.org/wiki/International_System_of_Units) */
    SI2 = 1 , /** MKS (m,kg,s) SI system */
    Imperial1 = 2, /** the Imperial system (http://en.wikipedia.org/wiki/Imperial_units) */
    ImperialDecimal = 3, /** Imperial with length in inch only */
    Centimeters = 4, /** All lengths in centimeters, areas and volumes in square/cubic meters */
    ImperialBuilding = 5, /** All lengths in feet + inches + fractions */
    MmMin = 6, /** Lengths in mm, Speed in mm/min. Angle in degrees. Useful for small parts & CNC */
    ImperialCivil = 7, /** Lengths in ft, Speed in ft/sec. Used in Civil Eng in North America */
    FemMilliMeterNewton = 8, /** Lengths in mm, Mass in t, TimeSpan in s, thus force is in N */
    NumUnitSystemTypes // must be the last item!
};

https://github.com/FreeCAD/FreeCAD/blob/ded17b7543c492d9051af454ced20781bbaa0d38/src/App/FreeCADInit.py#L899

# The values must match with that of the
# C++ enum class UnitSystem
class Scheme(IntEnum):
    SI1 = 0
    SI2 = 1
    Imperial1 = 2
    ImperialDecimal = 3
    Centimeters = 4
    ImperialBuilding = 5
    MmMin = 6
    ImperialCivil = 7
    FemMilliMeterNewton = 8

App.Units.Scheme = Scheme

So one could write

[qtyStr, standardUnitsPerTargetUnit, targetUnitStr] = App.Units.schemaTranslate( 
    App.Units.Quantity("1.0 m"), App.Units.Scheme.SI2 )
fc_unit = 1.0 / standardUnitsPerTargetUnit

instead of just hard-coding fc_unit = 0.001. For completeness,

paramGrp   = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units")
unitSchema = paramGrp.GetInt("UserSchema", 0)
# or equally
App.Units.getSchema()

retrieve the FreeCAD displayed unit system the user selected, and which are not used internally. The names can be listed via App.Units.listSchemas(), and the the user schema number can be resolved via App.Units.listSchemas(App.Units.getSchema()).

https://github.com/thliebig/CSXCAD/blob/cd9decb4d9cebe3c8bf115e2c0ee73f730f22da1/matlab/DefineRectGrid.m#L26

function CSX = DefineRectGrid(CSX, deltaUnit, mesh)
...
CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit = deltaUnit;

https://github.com/thliebig/openEMS/blob/a013077854a13b54ebea8aa87abb0dbfa9c96f58/matlab/AddWaveGuidePort.m#L65

port.drawingunit = CSX.RectilinearGrid.ATTRIBUTE.DeltaUnit;

https://github.com/thliebig/CSXCAD/blob/cd9decb4d9cebe3c8bf115e2c0ee73f730f22da1/matlab/ImportSTL.m#L6

function CSX = ImportSTL(CSX, propName, prio, filename, varargin)
...
% example:
%   CSX = ImportSTL(CSX, 'cad_model',10, 'sphere.stl','Transform',{'Scale',1/unit});
MisterHW commented 1 year ago

Before we get into improving output generation of S parameter scripts and e.g. move to using a "openems_fdtd" sub-directory so as to not spam the .FCStd directory, I think this is a good point to wrap up this PR.

Most importantly perhaps, getOrderedGridDefinitionsScriptLines() now strictly follows the priority level ordering of grid definitions.


Sample output with nested grid regions (only mediumMesh and fineMesh have the "remove lower-priority grid lines" option enabled. The userDefined field in extra is filled with

struct('x', [mesh.x], 'y', [mesh.y], 'z', [mesh.z 0.025])

resulting in:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% GRID LINES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% GRID - coarseMesh - simulationVolume
mesh.x = [mesh.x (-10.0:0.5:10.0) ];
mesh.y = [mesh.y (-15.0:0.5:15.0) ];
mesh.z = [mesh.z (0.0:0.5:15.0) ];
CSX = DefineRectGrid(CSX, unit, mesh);

%% GRID - mediumMesh - mediumVolume
mesh.x(mesh.x >= -2.0 & mesh.x <= 2.0) = [];
mesh.x = [mesh.x (-2.0:0.25:2.0) ];
mesh.y(mesh.y >= -4.5 & mesh.y <= 4.5) = [];
mesh.y = [mesh.y (-4.5:0.25:4.5) ];
mesh.z(mesh.z >= 0.0 & mesh.z <= 2.0) = [];
mesh.z = [mesh.z (0.0:0.25:2.0) ];
CSX = DefineRectGrid(CSX, unit, mesh);

%% GRID - fineMesh - fineVolume
mesh.x(mesh.x >= -1.1 & mesh.x <= 1.1) = [];
mesh.x = [mesh.x (-1.1:0.05:1.1) ];
mesh.y(mesh.y >= -4.0 & mesh.y <= 4.0) = [];
mesh.y = [mesh.y (-4.0:0.05:4.0) ];
mesh.z(mesh.z >= 0.0 & mesh.z <= 0.7) = [];
mesh.z = [mesh.z (0.0:0.05:0.7) ];
CSX = DefineRectGrid(CSX, unit, mesh);

%% GRID - extra - simulationVolume
mesh = struct('x', [mesh.x], 'y', [mesh.y], 'z', [mesh.z 0.025]);
CSX = DefineRectGrid(CSX, unit, mesh);
LubomirJagos commented 1 year ago

not anymore required as these changes were already merged in develop branch