femagtools 1.8.13__py3-none-any.whl → 1.8.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -381,7 +381,7 @@ def get_stator_dimensions(par, slots=[]):
381
381
  Da2 = Da1-2*airgap
382
382
  r = dict(
383
383
  lfe=round(lfe, 3),
384
- Dy1=round(Dy1, 3),
384
+ Dy1=round(Dy1, 3),
385
385
  Da1=round(Da1, 4),
386
386
  Da2=round(Da2, 4),
387
387
  ans=round(ans, 6),
@@ -546,9 +546,10 @@ def get_interior_magnet_dimensions(I1, N, kw, psi1, lfe, Da2, par):
546
546
  iron_shape=Da2/2))
547
547
 
548
548
 
549
- def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='rotorKs2'):
549
+ def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='statorRotor3'):
550
550
  r = dict()
551
551
  r['Da2'] = Da2
552
+ r['rotorfilfact']=0.95
552
553
  if 'Q2' not in par:
553
554
  r['num_slots'] = _rotor_slots(par['Q1'], par['p'])[0]
554
555
  else:
@@ -568,15 +569,42 @@ def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='rotorKs2'):
568
569
  taup = np.pi * Da2/(2*par['p'])
569
570
  hyr = psi1/2/lfe*par['By']
570
571
  r['Dy2'] = round(Da2 - 2*hr - 2*hyr, 4)
571
- logger.info("Dy2 %f Da2 %f hys %f hr %f",
572
- r['Dy2']*1e3, Da2*1e3, hyr*1e3, hr*1e3)
573
572
  slotwidth = 1e-3
574
573
  Q2 = r['num_slots']
575
574
  r1 = wr/2-slotwidth
576
575
  r2 = (Da2/2-hr-hs1)*np.tan(alfar)
576
+
577
577
  logger.info("Dy2 %f Da2 %f hys %f hr %f",
578
578
  r['Dy2']*1e3, Da2*1e3, hyr*1e3, hr*1e3)
579
- if rtype == 'statorRotor3':
579
+
580
+ # End-ring calculation
581
+ Ir_total_estimated = A * np.pi * Da2 / (2 * par['p'])
582
+ Ibar_eff = Ir_total_estimated / r['num_slots']
583
+
584
+ if r['num_slots'] > 0:
585
+ sin_term = np.sin(np.pi / r['num_slots'])
586
+ if sin_term != 0:
587
+ Iring_max_eff = Ibar_eff / (2 * sin_term)
588
+ else:
589
+ Iring_max_eff = 0
590
+ else:
591
+ Iring_max_eff = 0
592
+ if 'Jring' not in par:
593
+ par['Jring'] = par.get('J', 4e6)
594
+
595
+ Jring = par['Jring']
596
+
597
+ if 'kfilling_ring' not in par:
598
+ par['kfilling_ring'] = 1.0
599
+
600
+ kfilling_ring = par['kfilling_ring']
601
+
602
+ if Jring * kfilling_ring > 0:
603
+ Aring = Iring_max_eff / (Jring * kfilling_ring)
604
+ else:
605
+ Aring = 0
606
+
607
+ """if rtype == 'statorRotor3':
580
608
  r['statorRotor3'] = dict(
581
609
  slot_width=slotwidth,
582
610
  tooth_width=round(wt, 4),
@@ -589,6 +617,18 @@ def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='rotorKs2'):
589
617
  wedge_width1=0,
590
618
  wedge_width2=0,
591
619
  middle_line=0)
620
+ r['statorRotor3'] = dict(
621
+ slot_width=1e-3,
622
+ tooth_width=0,
623
+ slot_height=0.0157,
624
+ slot_top_sh=0,
625
+ slot_h1=0.5e-3,
626
+ slot_h2=2.2e-3,
627
+ slot_r1=2.2e-3,
628
+ slot_r2=1e-3,
629
+ wedge_width1=1e-3,
630
+ wedge_width2=0,
631
+ middle_line=0)
592
632
  elif rtype == 'rotorAsyn':
593
633
  r['rotorAsyn'] = dict(
594
634
  slot_bs2=0.1e-3,
@@ -612,8 +652,52 @@ def get_im_rotor_dimensions(A, Da2, psi1, lfe, par, rtype='rotorKs2'):
612
652
  slot_h2=0,
613
653
  slot_r1=1e-3, # r1,
614
654
  slot_r2=1e-3, # r2,
655
+ middle_line=0)"""
656
+
657
+ all_rotor_types = {}
658
+
659
+ all_rotor_types['statorRotor3'] = dict(
660
+ slot_width=slotwidth,
661
+ tooth_width=round(wt, 4),
662
+ slot_height=round(hr+r2, 4),
663
+ slot_top_sh=1.0,
664
+ slot_h1=round(hs1, 4),
665
+ slot_h2=round(hs1+r1, 4),
666
+ slot_r1=round(r1, 4),
667
+ slot_r2=round(r2),
668
+ wedge_width1=0,
669
+ wedge_width2=0,
615
670
  middle_line=0)
616
671
 
672
+ all_rotor_types['rotorAsyn'] = dict(
673
+ slot_bs2=0.1e-3,
674
+ slot_hs2=0.5e-3,
675
+ slot_b32=0.0,
676
+ slot_h32=0.0,
677
+ slot_b42=0.0,
678
+ slot_h42=0.0,
679
+ slot_b52=round(wr, 4),
680
+ slot_b62=3e-3,
681
+ slot_h52=2.5e-3,
682
+ slot_h62=round(hr, 4),
683
+ slot_h72=2e-3
684
+ )
685
+
686
+ all_rotor_types['rotorKs2'] = dict(
687
+ slot_angle=round(2 * alfar * 180 / np.pi, 2),
688
+ slot_height=round(hr + r1 + r2, 4),
689
+ slot_topwidth=round(wr, 4),
690
+ slot_width=slotwidth,
691
+ slot_h1=hs1,
692
+ slot_h2=0,
693
+ slot_r1=1e-3, # r1,
694
+ slot_r2=1e-3, # r2,
695
+ middle_line=0
696
+ )
697
+
698
+
699
+ r.update(all_rotor_types)
700
+ r['Aring'] = Aring
617
701
  return r
618
702
 
619
703
 
@@ -949,7 +1033,7 @@ def im(pnom: float, speed: float, p: int, **kwargs) -> dict:
949
1033
  slots = []
950
1034
  r = get_stator_dimensions(par, slots=slots)
951
1035
  # rotor parameters
952
- rtype = kwargs.get('rtype', 'rotorKs2')
1036
+ rtype = kwargs.get('rtype', 'statorRotor3')
953
1037
  r['rotor'] = get_im_rotor_dimensions(
954
1038
  par['cos_phi']*r['A'], r['Da2'], r['psi1'], r['lfe'],
955
1039
  par, rtype=rtype)
@@ -987,12 +1071,13 @@ def eesm(pnom: float, speed: float, p: int, **kwargs) -> dict:
987
1071
  r['name'] = f"EESM-{r['poles']}"
988
1072
  return r
989
1073
 
1074
+
990
1075
  if __name__ == "__main__":
991
1076
  # sizing example with SPM
992
1077
  pnom = 10e3 # shaft power in W
993
1078
  speed = 4400/60 # speed in 1/s
994
1079
  p = 4 # number of pole pairs
995
- udc = 600 # DC voltage in V
1080
+ udc = 600 # DC voltage in V
996
1081
 
997
1082
  r = spm(pnom, speed, p, udc=udc)
998
1083
 
femagtools/machine/sm.py CHANGED
@@ -7,7 +7,7 @@ import pathlib
7
7
  import numpy as np
8
8
  import scipy.optimize as so
9
9
  import scipy.interpolate as ip
10
- from .utils import skin_resistance, wdg_resistance, betai1, iqd, KTH, create_wdg
10
+ from .utils import skin_resistance, wdg_resistance, betai1, iqd, KTH, create_wdg, loss_models
11
11
  from .. import parstudy, windings
12
12
  import femagtools.bch
13
13
 
@@ -50,6 +50,7 @@ def parident(workdir, engine, machine,
50
50
  beta_max: maximal current angle (default 0°)
51
51
  num_move_steps: number of move steps
52
52
  cmd: femag command (default None, platform executable)
53
+ feloss: jordan, steinmetz, modified steinmetz, bertotti
53
54
  """
54
55
  cmd = kwargs.get('cmd', None)
55
56
 
@@ -125,6 +126,9 @@ def parident(workdir, engine, machine,
125
126
  period_frac=kwargs.get('period_frac', 6),
126
127
  speed=kwargs.get('speed', 50))
127
128
 
129
+ if kwargs.get("feloss", 0):
130
+ simulation["feloss"] = kwargs["feloss"]
131
+ machine["calc_fe_loss"] = loss_models[kwargs["feloss"].lower()]
128
132
  ###self.cleanup() # remove previously created files in workdir
129
133
  results = parvar(parvardef, machine, simulation, engine)
130
134
  b = results['f'][-1]
@@ -8,9 +8,16 @@ import numpy.linalg as la
8
8
  import scipy.interpolate as ip
9
9
  import logging
10
10
  from .. import parstudy, windings
11
+ from ..model import MachineModel
11
12
 
12
13
  logger = logging.getLogger(__name__)
13
14
 
15
+ loss_models = {
16
+ "modified steinmetz": 10,
17
+ "bertotti": 11,
18
+ "jordan": 1,
19
+ "steinmetz": 1
20
+ }
14
21
 
15
22
  def K(d):
16
23
  """space phasor transformation matrix
@@ -369,6 +376,7 @@ def dqparident(workdir, engine, temp, machine,
369
376
  period_frac: (int) fraction of rotating angle (default 6)
370
377
  dqtype: (str) type of identification: 'ldq' (default), 'psidq'
371
378
  cmd: femag executable
379
+ feloss: jordan, steinmetz, modified steinmetz, bertotti
372
380
  """
373
381
  import pathlib
374
382
 
@@ -390,21 +398,25 @@ def dqparident(workdir, engine, temp, machine,
390
398
  else:
391
399
  fcu = 0.42
392
400
 
393
- try: # calc basic dimensions if not fsl or dxf model
394
- from ..model import MachineModel
401
+ try:
395
402
  wdg = create_wdg(machine)
396
- Q1 = wdg.Q
397
- model = MachineModel(machine)
398
- Jmax = 30e6 # max current density in A/m2
399
- Acu = fcu*model.slot_area() # approx. copper area of one slot
400
- i1_max = round(g*Acu/wdg.l/N*Jmax/10)*10
401
403
  except KeyError:
402
- if kwargs.get('i1_max', 0) == 0:
403
- raise ValueError('i1_max missing')
404
+ pass
405
+ model = MachineModel(machine)
406
+ if kwargs.get('i1_max', 0):
404
407
  i1_max = kwargs['i1_max']
408
+ else:
409
+ try: # calc basic dimensions if not fsl or dxf model
410
+ Jmax = 30e6 # max current density in A/m2
411
+ Acu = fcu*model.slot_area() # approx. copper area of one slot
412
+ i1_max = round(g*Acu/wdg.l/N*Jmax/10)*10
413
+ except KeyError:
414
+ raise ValueError('i1_max missing')
405
415
 
406
416
  # winding resistance
407
417
  try:
418
+ da1 = machine['bore_diam']
419
+ hs = model.slot_height()
408
420
  if 'wire_gauge' in machine[wdgk]:
409
421
  aw = machine[wdgk]['wire_gauge']
410
422
  elif 'dia_wire' in machine[wdgk]:
@@ -412,10 +424,10 @@ def dqparident(workdir, engine, temp, machine,
412
424
  elif ('wire_width' in machine[wdgk]) and ('wire_height' in machine[wdgk]):
413
425
  aw = machine[wdgk]['wire_width']*machine[wdgk]['wire_height']
414
426
  else: # wire diameter from slot area
415
- da1 = machine['bore_diam']
427
+ Q1 = wdg.Q
416
428
  aw = 0.75 * fcu * np.pi*da1*hs/Q1/wdg.l/N
417
- r1 = wdg_resistance(wdg, N, g, aw, da1, hs, lfe)
418
- except (NameError, KeyError):
429
+ r1 = float(wdg_resistance(wdg, N, g, aw, da1, hs, lfe))
430
+ except (NameError, KeyError) as ex:
419
431
  r1 = 0 # cannot calc winding resistance
420
432
 
421
433
  n = len(temp)
@@ -483,6 +495,10 @@ def dqparident(workdir, engine, temp, machine,
483
495
  speed=kwargs.get('speed', defspeed),
484
496
  period_frac=period_frac)
485
497
 
498
+ if kwargs.get("feloss", 0):
499
+ simulation["feloss"] = kwargs["feloss"]
500
+ machine["calc_fe_loss"] = loss_models[kwargs["feloss"].lower()]
501
+
486
502
  # TODO: cleanup() # remove previously created files in workdir
487
503
  # start calculation
488
504
  results = parvar(parvardef, machine, simulation, engine)
@@ -490,7 +506,6 @@ def dqparident(workdir, engine, temp, machine,
490
506
  if 'poles' not in machine: # dxf model?
491
507
  machine['poles'] = 2*results['f'][0]['machine']['p']
492
508
  da1 = 2*results['f'][0]['machine']['fc_radius']
493
- wdg = create_wdg(machine)
494
509
  if 'bore_diam' in machine:
495
510
  da1 = machine['bore_diam']
496
511
  ls1 = 0
@@ -506,6 +521,14 @@ def dqparident(workdir, engine, temp, machine,
506
521
  except KeyError:
507
522
  rotor_mass = 0 # need femag classic > rel-9.3.x-48-gca42bbd0
508
523
 
524
+ if rotor_mass == 0:
525
+ try:
526
+ nc = parvar.femag.read_nc()
527
+ rotor_mass = float(sum(nc.get_mass()[1].values()))
528
+ logger.info("rotor mass from nc-file: %.1f kg", rotor_mass)
529
+ except StopIteration:
530
+ logger.warning("Could not read nc-file. Setting rotor_mass = 0!")
531
+
509
532
  dq = []
510
533
  if dqtype == 'ldq':
511
534
  for i in range(0, len(results['f']), 2):
@@ -585,7 +608,7 @@ def dqparident(workdir, engine, temp, machine,
585
608
  # diameter of wires
586
609
  aw = fcu*asl/Q1/nlayers/N
587
610
  hs = asl/(np.pi*da1/3)
588
- dqpars['r1'] = wdg_resistance(wdg, N, g, aw, da1, hs, lfe)
611
+ dqpars['r1'] = float(wdg_resistance(wdg, N, g, aw, da1, hs, lfe))
589
612
 
590
613
  if 'current_angles' in results['f'][0]:
591
614
  dqpars['current_angles'] = results['f'][0]['current_angles']
femagtools/multiproc.py CHANGED
@@ -235,6 +235,12 @@ class Engine:
235
235
  self.pool = None # garbage collector deletes threads
236
236
  return status
237
237
 
238
+ def read_nc(self):
239
+ """return a generator object of nc list"""
240
+ for t in self.job.tasks:
241
+ yield t.read_nc()
242
+ return None
243
+
238
244
  def stopThreads(self):
239
245
  """ stop all running treads
240
246
  """
femagtools/parstudy.py CHANGED
@@ -310,7 +310,7 @@ class ParameterStudy(object):
310
310
  set_magnet_properties(model, fea, self.femag.magnets)
311
311
  task.add_file(
312
312
  'femag.fsl',
313
- builder.create_model(model, self.femag.magnets) if not data_model_created else [] +
313
+ (builder.create_model(model, self.femag.magnets) if not data_model_created else []) +
314
314
  builder.create_analysis(fea) +
315
315
  ['save_model("close")'],
316
316
  append=data_model_created # model already created, append fsl
@@ -15,6 +15,3 @@ m.poison = ${model.get('poison', 0.3)}
15
15
  m.dampfact = ${model.get('dampfact', 0.0)}
16
16
  m.thcond = ${model.get('thcond', 30.0)}
17
17
  m.thcap = ${model.get('thcap', 480.0)}
18
-
19
- pre_models("FE-Losses-1")
20
- pre_models("FE-Losses-2")
@@ -6,8 +6,7 @@ muer = 1
6
6
  rel = 100
7
7
  cur = {1, 0}
8
8
  sigma1, sigma2 = get_dev_data( "cond_conduct" )
9
- --rotorbar = def_new_bar(xb,yb, "green", "U", cur[1],cur[2], "wi",
10
- rotorbar = def_new_bar(xcoil,ycoil, "green", "U", cur[1],cur[2], "wi", -- xcoil and ycoil defined in rotor_winding_ks2.mako
9
+ rotorbar = def_new_bar(xb,yb, "green", "U", cur[1],cur[2], "wi",
11
10
  sigma2, muer, rel, "polar", 0, 0)
12
11
  Abar = get_sreg_data("area",rotorbar)
13
12
 
@@ -38,4 +38,4 @@ m.load_ex_cur = ${model.get('load_ex_cur',0)}
38
38
  run_models("ld_lq_f_cur")
39
39
  %else:
40
40
  run_models("ld_lq_fast")
41
- %endif
41
+ %endif
@@ -19,7 +19,6 @@ if not airgap_created then
19
19
  end
20
20
  x1, y1 = pr2c(r1, alfa)
21
21
  n = math.floor(m.fc_radius*alfa/agndst + 1.5)
22
- n = n/10 -- Otherwise stacking mesh error TODO find a better formula
23
22
  nc_circle_m(r1, 0, x1, y1, 0.0, 0.0, n)
24
23
  if(m.npols_gen == m.num_poles) then
25
24
  nc_circle_m(x1, y1, r1, 0, 0.0, 0.0, n)
@@ -90,6 +90,7 @@ end
90
90
  if nrot == nil then
91
91
  nrot = nodes
92
92
  end
93
+ nrot=1 -- otherwise too long and only 1 is needed to have results
93
94
  curvec = {${','.join([str(x) for x in model['curvec']])}} -- A rms
94
95
 
95
96
  print("\nNo load flux simulation (DC) with rotation\n")
@@ -106,13 +107,9 @@ rotate({
106
107
  })
107
108
 
108
109
  file_psi = io.open("psi-rot-mag.dat","w")
109
- value={}
110
- temp={}
111
110
  for n=1,nrot+1 do
112
111
 
113
112
  pos, bag = calc_field(phi, curvec, file_psi)
114
- value[n]=#pos
115
- temp[n]=pos
116
113
  bags[n] = bag
117
114
  phi = n*dphi
118
115
  rotate({angle=phi, mode="absolute"})
@@ -120,40 +117,16 @@ end
120
117
  rotate({mode = "reset"}) -- restore the initial state (discard any changes)
121
118
  file_psi:close()
122
119
 
123
- -- add otherwise nil value in bags[n][i][k]
124
- for i=1, #value-1 do
125
- if value[i]<value[i+1] then
126
- num_pos=value[i]
127
- end
128
- end
129
- -- end
130
-
131
- --for i=1, #curvec do
132
- -- bagfile = string.format("noloadbag-%d.dat", i)
133
- -- file_bag = io.open(bagfile,"w")
134
- -- for k = 1, num_pos do --#pos do -- add otherwise nil value in bags[n][i][k]
135
- -- file_bag:write(string.format("%g ", pos[k]))
136
- -- for n=1, nrot+1 do
137
- -- file_bag:write(string.format("%g ",
138
- -- bags[n][i][k])) -- Br, rotpos, cur, pos
139
- -- end
140
- -- file_bag:write("\n")
141
- -- end
142
- -- file_bag:close()
143
- --end
144
-
145
- -- add otherwise nil value in bags[n][i][k]
146
120
  for i=1, #curvec do
147
121
  bagfile = string.format("noloadbag-%d.dat", i)
148
122
  file_bag = io.open(bagfile,"w")
149
- for n=1, nrot+1 do
150
- for k = 1, value[n] do --#pos do
151
- pos=temp[n]
152
- file_bag:write(string.format("%g ", pos[k]))
123
+ for k = 1, #pos do
124
+ file_bag:write(string.format("%g ", pos[k]))
125
+ for n=1, nrot+1 do
153
126
  file_bag:write(string.format("%g ",
154
127
  bags[n][i][k])) -- Br, rotpos, cur, pos
155
128
  end
156
129
  file_bag:write("\n")
157
130
  end
158
131
  file_bag:close()
159
- end
132
+ end
@@ -52,7 +52,7 @@ nc_circle_m(xw2, yw2, x8, y8, 0,0,0)
52
52
  nc_circle_m(x4, y4, xw3, yw3, 0,0,0)
53
53
  nc_circle_m(xw4, yw4, x7, y7, 0,0,0)
54
54
 
55
- x, y = (x7+x5)/2, (y4+y6)/2
55
+ x, y = (x4+x7)/2, (y4+y5)/2
56
56
  create_mesh_se(x, y)
57
57
 
58
58
  def_new_sreg(x, y, 'StZa', 'skyblue')
femagtools/utils.py CHANGED
@@ -10,7 +10,10 @@ def fft(pos, y, pmod=0):
10
10
  pmod: number of poles in model (ignored if 0)
11
11
  """
12
12
  model_angle = pos[-1] - pos[0]
13
- ntiles = int(round(360/model_angle))
13
+ if 360/model_angle < 1:
14
+ ntiles=1
15
+ else:
16
+ ntiles = int(round(360/model_angle))
14
17
 
15
18
  if pmod:
16
19
  negative_periodic = pmod % 2
@@ -51,6 +54,7 @@ def fft(pos, y, pmod=0):
51
54
  alfa0 = np.angle(Y[i])
52
55
  alfa = np.angle(Y[:nmax])
53
56
 
57
+
54
58
  return {'a': a, 'a0': a0, 'T0': T0, 'alfa0': alfa0,
55
59
  'alfa': alfa,
56
60
  'nue': (2*np.abs(Y[:nmax])/N).tolist(),
@@ -1,14 +1,42 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: femagtools
3
- Version: 1.8.13
3
+ Version: 1.8.14
4
4
  Summary: Python API for FEMAG
5
5
  Author-email: Ronald Tanner <tar@semafor.ch>, Dapu Zhang <dzhang@gtisoft.com>, Beat Holm <hob@semafor.ch>, Günther Amsler <amg@semafor.ch>, Nicolas Mauchle <mau@semafor.ch>
6
- License-Expression: BSD-3-Clause
6
+ License: Copyright (c) 2016-2023, Semafor Informatik & Energie AG, Basel
7
+ Copyright (c) 2023-2024, Gamma Technology LLC
8
+ All rights reserved.
9
+
10
+ Redistribution and use in source and binary forms, with or without
11
+ modification, are permitted provided that the following conditions
12
+ are met:
13
+
14
+ Redistributions of source code must retain the above copyright notice,
15
+ this list of conditions and the following disclaimer.
16
+
17
+ Redistributions in binary form must reproduce the above copyright notice,
18
+ this list of conditions and the following disclaimer in the documentation
19
+ and/or other materials provided with the distribution.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
25
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31
+ THE POSSIBILITY OF SUCH DAMAGE.
32
+
7
33
  Classifier: Programming Language :: Python :: 3
8
34
  Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: BSD License
9
36
  Classifier: Topic :: Scientific/Engineering
10
37
  Requires-Python: >=3.7
11
38
  Description-Content-Type: text/markdown
39
+ License-File: LICENSE
12
40
  Requires-Dist: numpy
13
41
  Requires-Dist: scipy
14
42
  Requires-Dist: mako
@@ -36,6 +64,7 @@ Provides-Extra: test
36
64
  Requires-Dist: pytest; extra == "test"
37
65
  Provides-Extra: all
38
66
  Requires-Dist: femagtools[dxfsl,meshio,mplot,svgfsl,test,vtk,zmq]; extra == "all"
67
+ Dynamic: license-file
39
68
 
40
69
 
41
70
  # Introduction to Femagtools